From: Youhei SASAKI <uwabami@gfd-dennou.org>
Date: Wed, 10 Dec 2025 15:08:20 +0900
Subject: Apply 24-bit-color.patch

Signed-off-by: Youhei SASAKI <uwabami@gfd-dennou.org>
---
 README.configure      | 18 ++++++++--
 config.h.in           |  3 ++
 configure             | 17 ++++++++++
 configure.ac          | 10 ++++++
 src/command.C         | 32 +++++++++++++++---
 src/init.C            | 20 +++++------
 src/main.C            | 28 ++++++++--------
 src/rxvt.h            | 92 +++++++++++++++++++++++++++++++--------------------
 src/rxvtfont.C        | 26 +++++++--------
 src/rxvtfont.h        |  6 ++--
 src/rxvtperl.xs       | 52 ++++++++++++++++++-----------
 src/screen.C          | 71 ++++++++++++++++++++++++++++++---------
 src/scrollbar-next.C  |  8 ++---
 src/scrollbar-plain.C |  2 +-
 src/scrollbar-rxvt.C  |  6 ++--
 src/scrollbar-xterm.C |  6 ++--
 src/scrollbar.C       |  4 +--
 17 files changed, 270 insertions(+), 131 deletions(-)

diff --git a/README.configure b/README.configure
index 6c5d007..9f65d3c 100644
--- a/README.configure
+++ b/README.configure
@@ -9,8 +9,8 @@ CONFIGURE OPTIONS
 
     --enable-everything
         Add (or remove) support for all non-multichoice options listed in
-        "./configure --help", except for "--enable-assert" and
-        "--enable-256-color".
+        "./configure --help", except for "--enable-assert",
+        "--enable-256-color" and "--enable-24-bit-color".
 
         You can specify this and then disable options you do not like by
         *following* this with the appropriate "--disable-..." arguments, or
@@ -241,6 +241,20 @@ CONFIGURE OPTIONS
         dramatically when more than six fonts are in use by a terminal
         instance.
 
+    --enable-24-bit-color (default: off)
+        Enable use of 24-bit colors through
+            SGR 38 ; 2 ; R ; G ; B  m
+            SGR 48 ; 2 ; R ; G ; B  m
+
+        This switch should break termcap/terminfo compatibility to
+        "TERM=rxvt-unicode-256color", and consequently set "TERM" to
+        "rxvt-unicode-24bit" by default but there is no termcap/terminfo
+        for 24-bit color support
+
+        It also results in higher memory usage and can slow down urxvt
+        dramatically when more than six fonts are in use by a terminal
+        instance.
+
     --with-name=NAME (default: urxvt)
         Set the basename for the installed binaries, resulting in "urxvt",
         "urxvtd" etc.). Specify "--with-name=rxvt" to replace with "rxvt".
diff --git a/config.h.in b/config.h.in
index 18e6d56..934b2f8 100644
--- a/config.h.in
+++ b/config.h.in
@@ -291,6 +291,9 @@
 /* Define if you want 256-color support */
 #undef USE_256_COLORS
 
+/* Define if you want 24-bit color support */
+#undef USE_24_BIT_COLOR
+
 /* Enable extensions on AIX 3, Interix.  */
 #ifndef _ALL_SOURCE
 # undef _ALL_SOURCE
diff --git a/configure b/configure
index 89eec39..55b8796 100755
--- a/configure
+++ b/configure
@@ -734,6 +734,7 @@ enable_everything
 enable_assert
 enable_warnings
 enable_256_color
+enable_24_bit_color
 enable_unicode3
 enable_combining
 enable_xft
@@ -1408,6 +1409,7 @@ Optional Features:
   --enable-assert         enable assertions
   --enable-warnings       turn on g++ warnings
   --enable-256-color      enable 256-color support
+  --enable-24-bit-color   enable 24-bit color support
   --enable-unicode3       use 21 instead of 16 bits to represent unicode characters
   --enable-combining      enable composition of base and combining characters
   --enable-xft            enable xft support on systems that have it
@@ -5129,6 +5131,21 @@ printf "%s\n" "#define USE_256_COLORS 1" >>confdefs.h
 
 fi
 
+support_24_bit_color=no
+# Check whether --enable-24-bit-color was given.
+if test ${enable_24_bit_color+y}
+then :
+  enableval=$enable_24_bit_color; if test x$enableval = xyes; then
+    support_24_bit_color=yes
+  fi
+fi
+
+if test x$support_24_bit_color = xyes; then
+
+printf "%s\n" "#define USE_24_BIT_COLOR 1" >>confdefs.h
+
+fi
+
 # Check whether --enable-unicode3 was given.
 if test ${enable_unicode3+y}
 then :
diff --git a/configure.ac b/configure.ac
index 9ce4f4b..e1e9416 100644
--- a/configure.ac
+++ b/configure.ac
@@ -197,6 +197,16 @@ if test x$support_256_color = xyes; then
   AC_DEFINE(USE_256_COLORS, 1, Define if you want 256-color support)
 fi
 
+support_24_bit_color=no
+AC_ARG_ENABLE(24-bit-color,
+  [  --enable-24-bit-color   enable 24-bit color support],
+  [if test x$enableval = xyes; then
+    support_24_bit_color=yes
+  fi])
+if test x$support_24_bit_color = xyes; then
+  AC_DEFINE(USE_24_BIT_COLOR, 1, Define if you want 24-bit color support)
+fi
+
 AC_ARG_ENABLE(unicode3,
   [  --enable-unicode3       use 21 instead of 16 bits to represent unicode characters],
   [if test x$enableval = xyes -o x$enableval = xno; then
diff --git a/src/command.C b/src/command.C
index 69ec0ee..f8acee9 100644
--- a/src/command.C
+++ b/src/command.C
@@ -1824,11 +1824,11 @@ rxvt_term::update_fade_color (unsigned int idx, bool first_time)
   if (rs[Rs_fade])
     {
       if (!first_time)
-        pix_colors_focused [idx].free (this);
+        lookup_color(idx, pix_colors_focused).free (this);
 
       rgba c;
-      pix_colors [Color_fade].get (c);
-      pix_colors_focused [idx].fade (this, atoi (rs[Rs_fade]), pix_colors_unfocused [idx], c);
+      lookup_color(Color_fade, pix_colors).get (c);
+      lookup_color(idx, pix_colors_focused).fade (this, atoi (rs[Rs_fade]), lookup_color(idx, pix_colors_unfocused), c);
     }
 #endif
 }
@@ -3424,7 +3424,7 @@ rxvt_term::process_color_seq (int report, int color, const char *str, string_ter
   if (str[0] == '?' && !str[1])
     {
       rgba c;
-      pix_colors_focused[color].get (c);
+      lookup_color(color, pix_colors_focused).get (c);
       char rgba_str[32];
 
 #if XFT
@@ -3933,7 +3933,7 @@ rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
 {
   unsigned int i;
   short rendset;
-  int rendstyle;
+  rend_t rendstyle;
 
   if (nargs == 0)
     {
@@ -4039,6 +4039,20 @@ rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
             break;
 
           case 38: // set fg color, ISO 8613-6
+#if USE_24_BIT_COLOR
+            if (nargs > i + 2 && arg[i + 1] == 5)
+              {
+                scr_color ((unsigned int) (minCOLOR + arg[i + 2]), Color_fg);
+                i += 2;
+              }
+            else if (nargs > i + 4 && arg[i + 1] == 2)
+              {
+                unsigned int r = arg[i + 2], g = arg[i + 3], b = arg[i + 4];
+                scr_color_rgb (r, g, b, Color_fg);
+                i += 4;
+              }
+#endif
+            break;
           case 48: // set bg color, ISO 8613-6
             {
               unsigned int fgbg = arg[i] == 38 ? Color_fg : Color_bg;
@@ -4051,6 +4065,14 @@ rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
 
                   scr_color (idx, fgbg);
                 }
+#if USE_24_BIT_COLOR
+              else if (nargs > i + 4 && arg[i + 1] == 2)
+                {
+                  unsigned int r = arg[i + 2], g = arg[i + 3], b = arg[i + 4];
+                  scr_color_rgb (r, g, b, Color_bg);
+                  i += 4;
+                }
+#endif
               else if (nargs > i + 4 && arg[i + 1] == 2)
                 {
                   unsigned int r = arg[i + 2];
diff --git a/src/init.C b/src/init.C
index 907cdab..d6b484e 100644
--- a/src/init.C
+++ b/src/init.C
@@ -1138,7 +1138,7 @@ rxvt_term::get_colors ()
 
   for (i = 0; i < NRS_COLORS; i++)
     if (const char *name = rs[Rs_color + i])
-      set_color (pix_colors [i], name);
+      set_color (lookup_color(i, pix_colors), name);
 
   /*
    * get scrollBar shadow colors
@@ -1147,13 +1147,13 @@ rxvt_term::get_colors ()
    * from the fvwm window manager.
    */
 #ifdef RXVT_SCROLLBAR
-  pix_colors [Color_scroll].fade (this, 50, pix_colors [Color_bottomShadow]);
+  lookup_color(Color_scroll, pix_colors).fade (this, 50, lookup_color(Color_bottomShadow, pix_colors));
 
   rgba cscroll;
-  pix_colors [Color_scroll].get (cscroll);
+  lookup_color(Color_scroll, pix_colors).get (cscroll);
 
   /* topShadowColor */
-  if (!pix_colors[Color_topShadow].set (this,
+  if (!lookup_color(Color_topShadow, pix_colors).set (this,
                    rgba (
                      min ((int)rgba::MAX_CC, max (cscroll.r / 5, cscroll.r) * 7 / 5),
                      min ((int)rgba::MAX_CC, max (cscroll.g / 5, cscroll.g) * 7 / 5),
@@ -1395,8 +1395,8 @@ rxvt_term::create_windows (int argc, const char *const *argv)
   window_calc (0, 0);
 
   /* sub-window placement & size in rxvt_term::resize_all_windows () */
-  attributes.background_pixel = pix_colors_focused [Color_border];
-  attributes.border_pixel     = pix_colors_focused [Color_border];
+  attributes.background_pixel = lookup_color(Color_border, pix_colors_focused);
+  attributes.border_pixel     = lookup_color(Color_border, pix_colors_focused);
   attributes.colormap         = cmap;
 
   top = XCreateWindow (dpy, parent,
@@ -1493,8 +1493,8 @@ rxvt_term::create_windows (int argc, const char *const *argv)
                             window_vt_x, window_vt_y,
                             vt_width, vt_height,
                             0,
-                            pix_colors_focused[Color_fg],
-                            pix_colors_focused[Color_bg]);
+                            lookup_color(Color_fg, pix_colors_focused),
+                            lookup_color(Color_bg, pix_colors_focused));
 
   attributes.bit_gravity = NorthWestGravity;
   XChangeWindowAttributes (dpy, vt, CWBitGravity, &attributes);
@@ -1511,8 +1511,8 @@ rxvt_term::create_windows (int argc, const char *const *argv)
   vt_ev.start (display, vt);
 
   /* graphics context for the vt window */
-  gcvalue.foreground         = pix_colors[Color_fg];
-  gcvalue.background         = pix_colors[Color_bg];
+  gcvalue.foreground         = lookup_color(Color_fg, pix_colors);
+  gcvalue.background         = lookup_color(Color_bg, pix_colors);
   gcvalue.graphics_exposures = 0;
 
   gc = XCreateGC (dpy, vt,
diff --git a/src/main.C b/src/main.C
index 10e5065..e635231 100644
--- a/src/main.C
+++ b/src/main.C
@@ -251,10 +251,10 @@ rxvt_term::~rxvt_term ()
       for (int i = 0; i < TOTAL_COLORS; i++)
         if (ISSET_PIXCOLOR (i))
           {
-            pix_colors_focused   [i].free (this);
+            lookup_color(i, pix_colors_focused).free (this);
 #if OFF_FOCUS_FADING
             if (rs[Rs_fade])
-              pix_colors_unfocused [i].free (this);
+              lookup_color(i, pix_colors_unfocused).free (this);
 #endif
           }
 
@@ -970,8 +970,8 @@ rxvt_term::set_window_color (int idx, const char *color)
         }
     }
 
-  pix_colors_focused[idx].free (this);
-  set_color (pix_colors_focused[idx], color);
+  lookup_color(idx, pix_colors_focused).free (this);
+  set_color (lookup_color(idx, pix_colors_focused), color);
 
 done:
   /*TODO: handle Color_BD, scrollbar background, etc. */
@@ -988,12 +988,12 @@ rxvt_term::recolor_cursor ()
   XColor fg, bg;
 
   (ISSET_PIXCOLOR (Color_pointer_fg)
-     ? pix_colors_focused[Color_pointer_fg]
-     : pix_colors_focused[Color_fg]).get (fg);
+     ? lookup_color(Color_pointer_fg, pix_colors_focused)
+     : lookup_color(Color_fg, pix_colors_focused)).get (fg);
 
   (ISSET_PIXCOLOR (Color_pointer_bg)
-     ? pix_colors_focused[Color_pointer_bg]
-     : pix_colors_focused[Color_bg]).get (bg);
+     ? lookup_color(Color_pointer_bg, pix_colors_focused)
+     : lookup_color(Color_bg, pix_colors_focused)).get (bg);
 
   XRecolorCursor (dpy, TermWin_cursor, &fg, &bg);
 }
@@ -1012,14 +1012,14 @@ rxvt_term::get_colorfgbg ()
   char *env_colorfgbg;
 
   for (i = Color_Black; i <= Color_White; i++)
-    if (pix_colors[Color_fg] == pix_colors[i])
+    if (lookup_color(Color_fg, pix_colors) == lookup_color(i, pix_colors))
       {
         sprintf (fstr, "%d", i - Color_Black);
         break;
       }
 
   for (i = Color_Black; i <= Color_White; i++)
-    if (pix_colors[Color_bg] == pix_colors[i])
+    if (lookup_color(Color_bg, pix_colors) == lookup_color(i, pix_colors))
       {
         sprintf (bstr, "%d", i - Color_Black);
 #if HAVE_IMG
@@ -1048,8 +1048,8 @@ rxvt_term::set_color (rxvt_color &color, const char *name)
 void
 rxvt_term::alias_color (int dst, int src)
 {
-  pix_colors[dst].free (this);
-  pix_colors[dst].set (this, rs[Rs_color + dst] = rs[Rs_color + src]);
+  lookup_color(dst, pix_colors).free (this);
+  lookup_color(dst, pix_colors).set (this, rs[Rs_color + dst] = rs[Rs_color + src]);
 }
 
 #ifdef SMART_RESIZE
@@ -1203,8 +1203,8 @@ rxvt_term::set_widthheight (unsigned int newwidth, unsigned int newheight)
 void
 rxvt_term::im_set_color (unsigned long &fg, unsigned long &bg)
 {
-  fg = pix_colors [Color_fg];
-  bg = pix_colors [Color_bg];
+  fg = lookup_color(Color_fg, pix_colors);
+  bg = lookup_color(Color_bg, pix_colors);
 }
 
 void
diff --git a/src/rxvt.h b/src/rxvt.h
index 37aba23..f7f1abd 100644
--- a/src/rxvt.h
+++ b/src/rxvt.h
@@ -41,7 +41,11 @@ typedef uint32_t text_t;
 #else
 typedef uint16_t text_t; // saves lots of memory
 #endif
+#if USE_24_BIT_COLOR
+typedef uint64_t rend_t;
+#else
 typedef uint32_t rend_t;
+#endif
 typedef  int32_t tlen_t;  // was int16_t, but this results in smaller code and memory use
 typedef  int32_t tlen_t_; // specifically for use in the line_t structure
 
@@ -351,46 +355,55 @@ enum {
 #define RS_None                 0
 
 // GET_BGATTR depends on RS_fgShift > RS_bgShift
-#define RS_colorMask		((1UL << Color_Bits) - 1UL)
-#define RS_bgShift		0
-#define RS_fgShift		(RS_bgShift + Color_Bits)
-#define RS_bgMask               (RS_colorMask << RS_bgShift)
-#define RS_fgMask               (RS_colorMask << RS_fgShift)
+#define RS_colorMask		    ((rend_t) ((1UL << Color_Bits) - 1UL))
+#define RS_bgShift		        0
+#define RS_fgShift		        (RS_bgShift + Color_Bits)
+#define RS_bgMask               ((rend_t) (RS_colorMask << RS_bgShift))
+#define RS_fgMask               ((rend_t) (RS_colorMask << RS_fgShift))
 
 // must have space for rxvt_fontset::fontCount * 2 + 2 values
 #define RS_fontShift            (RS_fgShift + Color_Bits)
-#define RS_Careful		(1UL << RS_fontShift)	/* be careful when drawing these */
-#define RS_fontCount		rxvt_fontset::fontCount
-#define RS_fontMask             ((RS_fontCount << (RS_fontShift + 1)) | RS_Careful)   // includes RS_Careful
+#define RS_Careful		        ((rend_t) (1UL << RS_fontShift))	/* be careful when drawing these */
+#define RS_fontCount		    ((rend_t) rxvt_fontset::fontCount)
+#define RS_fontMask             ((rend_t) ((RS_fontCount << (RS_fontShift + 1)) | RS_Careful))   // includes RS_Careful
 
 // toggle this to force redraw, must be != RS_Careful and otherwise "pretty neutral"
-#define RS_redraw		(2UL << RS_fontShift)
+#define RS_redraw		        ((rend_t) (2UL << RS_fontShift))
 
-#define RS_Sel                  (1UL << 22)
+#if USE_24_BIT_COLOR
+# define RS_fontCountSize 4
+#elif USE_256_COLORS
+# define RS_fontCountSize 4
+#else
+# define RS_fontCountSize 8
+#endif
+
+#define RS_selShift             (RS_fontShift + RS_fontCountSize)
+#define RS_Sel                  ((rend_t) (1UL << RS_selShift))
 
 // 4 custom bits for extensions
 #define RS_customCount          16UL
-#define RS_customShift          23
-#define RS_customMask           ((RS_customCount - 1UL) << RS_customShift)
+#define RS_customShift          (RS_selShift + 1)
+#define RS_customMask           ((rend_t) ((RS_customCount - 1UL) << RS_customShift))
 
 // font styles
-#define RS_Bold                 (1UL << RS_styleShift)
-#define RS_Italic		(2UL << RS_styleShift)
+#define RS_Bold                 ((rend_t) (1UL << RS_styleShift))
+#define RS_Italic		        ((rend_t) (2UL << RS_styleShift))
 
-#define RS_styleCount		4
-#define RS_styleShift		27
-#define RS_styleMask		(RS_Bold | RS_Italic)
+#define RS_styleCount		    4
+#define RS_styleShift	        (RS_customShift + RS_styleCount)
+#define RS_styleMask		    ((rend_t) (RS_Bold | RS_Italic))
 
 // fake styles
-#define RS_Blink                (1UL << 29)
-#define RS_RVid                 (1UL << 30)    // reverse video
-#define RS_Uline                (1UL << 31)    // underline
+#define RS_Blink                ((rend_t) (1UL << (RS_styleShift + 2)))
+#define RS_RVid                 ((rend_t) (1UL << (RS_styleShift + 3)))    // reverse video
+#define RS_Uline                ((rend_t) (1UL << (RS_styleShift + 4)))    // underline
 
-#define RS_baseattrMask         (RS_Italic | RS_Bold | RS_Blink | RS_RVid | RS_Uline)
-#define RS_attrMask             (RS_baseattrMask | RS_fontMask)
+#define RS_baseattrMask         ((rend_t) (RS_Italic | RS_Bold | RS_Blink | RS_RVid | RS_Uline))
+#define RS_attrMask             ((rend_t) (RS_baseattrMask | RS_fontMask))
 
 #define DEFAULT_RSTYLE  (RS_None | (Color_fg    << RS_fgShift) | (Color_bg     << RS_bgShift))
-#define OVERLAY_RSTYLE  (RS_None | (Color_Black << RS_fgShift) | (Color_Yellow << RS_bgShift))
+#define OVERLAY_RSTYLE  (RS_None | (Color_White << RS_fgShift) | (Color_Blue << RS_bgShift))
 
 enum {
   C0_NUL = 0x00,
@@ -544,7 +557,9 @@ enum colour_list {
 #endif
 };
 
-#if USE_256_COLORS
+#if USE_24_BIT_COLOR
+# define Color_Bits      25
+#elif USE_256_COLORS
 # define Color_Bits      9 // 0 .. maxTermCOLOR24
 #else
 # define Color_Bits      8 // 0 .. maxTermCOLOR24
@@ -680,21 +695,21 @@ typedef struct _mwmhints
 #define ROW(n) ROW_of (this, n)
 
 /* how to build & extract colors and attributes */
-#define GET_BASEFG(x)           (((x) & RS_fgMask) >> RS_fgShift)
-#define GET_BASEBG(x)           (((x) & RS_bgMask) >> RS_bgShift)
+#define GET_BASEFG(x)           ((((rend_t) (x)) & RS_fgMask) >> RS_fgShift)
+#define GET_BASEBG(x)           ((((rend_t) (x)) & RS_bgMask) >> RS_bgShift)
 
-#define GET_FONT(x)             (((x) & RS_fontMask) >> RS_fontShift)
-#define SET_FONT(x,fid)         (((x) & ~RS_fontMask) | ((fid) << RS_fontShift))
+#define GET_FONT(x)             ((((rend_t) (x)) & RS_fontMask) >> RS_fontShift)
+#define SET_FONT(x,fid)         ((((rend_t) (x)) & ~((rend_t) RS_fontMask)) | (((rend_t) (fid)) << RS_fontShift))
 
-#define GET_STYLE(x)		(((x) & RS_styleMask) >> RS_styleShift)
-#define SET_STYLE(x,style)	(((x) & ~RS_styleMask) | ((style) << RS_styleShift))
+#define GET_STYLE(x)		    ((((rend_t) (x)) & RS_styleMask) >> RS_styleShift)
+#define SET_STYLE(x,style)	    ((((rend_t) (x)) & ~((rend_t) RS_styleMask)) | (((rend_t) (style)) << RS_styleShift))
 
-#define GET_ATTR(x)             (((x) & RS_attrMask))
-#define SET_FGCOLOR(x,fg)       (((x) & ~RS_fgMask)   | ((fg) << RS_fgShift))
-#define SET_BGCOLOR(x,bg)       (((x) & ~RS_bgMask)   | ((bg) << RS_bgShift))
-#define SET_ATTR(x,a)           (((x) & ~RS_attrMask) | (a))
+#define GET_ATTR(x)             ((((rend_t) (x)) & RS_attrMask))
+#define SET_FGCOLOR(x,fg)       ((((rend_t) (x)) & ~((rend_t) RS_fgMask))   | (((rend_t) (fg)) << RS_fgShift))
+#define SET_BGCOLOR(x,bg)       ((((rend_t) (x)) & ~((rend_t) RS_bgMask))   | (((rend_t) (bg)) << RS_bgShift))
+#define SET_ATTR(x,a)           ((((rend_t) (x)) & ~((rend_t) RS_attrMask)) | ((rend_t) (a)))
 
-#define RS_SAME(a,b)		(!(((a) ^ (b)) & ~RS_Careful))
+#define RS_SAME(a,b)		    (!((((rend_t) (a)) ^ ((rend_t) (b))) & ~((rend_t) RS_Careful)))
 
 #define PIXCOLOR_NAME(idx)      rs[Rs_color + (idx)]
 #define ISSET_PIXCOLOR(idx)     (!!rs[Rs_color + (idx)])
@@ -1494,7 +1509,12 @@ struct rxvt_term : zero_initialized, rxvt_vars, rxvt_screen
   void scr_swap_screen () noexcept;
   void scr_change_screen (int scrn);
   void scr_color (unsigned int color, int fgbg) noexcept;
-  void scr_rendition (int set, int style) noexcept;
+#if USE_24_BIT_COLOR
+  void scr_color_24 (unsigned int color, int fgbg) noexcept;
+  void scr_color_rgb (unsigned int r, unsigned int g, unsigned int b, int fgbg) noexcept;
+#endif
+  rxvt_color &lookup_color (unsigned int color, rxvt_color *table) noexcept;
+  void scr_rendition (int set, rend_t style) noexcept;
   void scr_add_lines (const wchar_t *str, int len, int minlines = 0) noexcept;
   void scr_backspace () noexcept;
   void scr_tab (int count, bool ht = false) noexcept;
diff --git a/src/rxvtfont.C b/src/rxvtfont.C
index 2a36e96..42565d7 100644
--- a/src/rxvtfont.C
+++ b/src/rxvtfont.C
@@ -243,20 +243,20 @@ rxvt_font::clear_rect (rxvt_drawable &d, int x, int y, int w, int h, int color)
 
 # ifdef HAVE_IMG
       if (term->bg_img
-          && !term->pix_colors[color].is_opaque ()
+          && !term->lookup_color(color, term->pix_colors).is_opaque ()
           && ((dst = XftDrawPicture (d))))
         {
           XClearArea (disp, d, x, y, w, h, false);
 
-          Picture solid_color_pict = XftDrawSrcPicture (d, &term->pix_colors[color].c);
+          Picture solid_color_pict = XftDrawSrcPicture (d, &term->lookup_color(color, term->pix_colors).c);
           XRenderComposite (disp, PictOpOver, solid_color_pict, None, dst, 0, 0, 0, 0, x, y, w, h);
         }
       else
 # endif
-        XftDrawRect (d, &term->pix_colors[color].c, x, y, w, h);
+        XftDrawRect (d, &term->lookup_color(color, term->pix_colors).c, x, y, w, h);
 
 #else
-      XSetForeground (disp, gc, term->pix_colors[color]);
+      XSetForeground (disp, gc, term->lookup_color(color, term->pix_colors));
       XFillRectangle (disp, d, gc, x, y, w, h);
 #endif
     }
@@ -343,7 +343,7 @@ rxvt_font_default::draw (rxvt_drawable &d, int x, int y,
 
   clear_rect (d, x, y, term->fwidth * len, term->fheight, bg);
 
-  XSetForeground (disp, gc, term->pix_colors[fg]);
+  XSetForeground (disp, gc, term->lookup_color(fg, term->pix_colors));
 
   while (len)
     {
@@ -1065,7 +1065,7 @@ rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
   int base = ascent; // sorry, incorrect: term->fbase;
 
   XGCValues v;
-  v.foreground = term->pix_colors[fg];
+  v.foreground = term->lookup_color(fg, term->pix_colors);
   v.font = f->fid;
 
   if (enc2b)
@@ -1074,7 +1074,7 @@ rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
 
       if (bg == Color_bg && !slow)
         {
-          v.background = term->pix_colors[bg];
+          v.background = term->lookup_color(bg, term->pix_colors);
           XChangeGC (disp, gc, GCForeground | GCBackground | GCFont, &v);
           XDrawImageString16 (disp, d, gc, x, y + base, xc, len);
         }
@@ -1106,7 +1106,7 @@ rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
 
       if (bg == Color_bg && !slow)
         {
-          v.background = term->pix_colors[bg];
+          v.background = term->lookup_color(bg, term->pix_colors);
           XChangeGC (disp, gc, GCForeground | GCBackground | GCFont, &v);
           XDrawImageString (disp, d, gc, x, y + base, xc, len);
         }
@@ -1528,7 +1528,7 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
 
           if (term->bg_img
               && (bg == Color_transparent || bg == Color_bg
-                  || (bg >= 0 && !term->pix_colors[bg].is_opaque () && ((dst = XftDrawPicture (d2))))))
+                  || (bg >= 0 && !term->lookup_color(bg, term->pix_colors).is_opaque () && ((dst = XftDrawPicture (d2))))))
             {
               int src_x = x, src_y = y;
 
@@ -1565,7 +1565,7 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
 
               if (dst)
                 {
-                  Picture solid_color_pict = XftDrawSrcPicture (d2, &term->pix_colors[bg].c);
+                  Picture solid_color_pict = XftDrawSrcPicture (d2, &term->lookup_color(bg, term->pix_colors).c);
 
                   // dst can only be set when bg >= 0
                   XRenderComposite (disp, PictOpOver, solid_color_pict, None, dst, 0, 0, 0, 0, 0, 0, w, h);
@@ -1573,9 +1573,9 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
             }
           else
 #endif
-            XftDrawRect (d2, &term->pix_colors[bg >= 0 ? bg : Color_bg].c, 0, 0, w, h);
+            XftDrawRect (d2, &term->lookup_color(bg >= 0 ? bg : Color_bg, term->pix_colors).c, 0, 0, w, h);
 
-          XftDrawGlyphSpec (d2, &term->pix_colors[fg].c, f, enc, ep - enc);
+          XftDrawGlyphSpec (d2, &term->lookup_color(fg, term->pix_colors).c, f, enc, ep - enc);
           XCopyArea (disp, d2, d, gc, 0, 0, w, h, x, y);
         }
       else
@@ -1584,7 +1584,7 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
   else
     {
       clear_rect (d, x, y, w, h, bg);
-      XftDrawGlyphSpec (d, &term->pix_colors[fg].c, f, enc, ep - enc);
+      XftDrawGlyphSpec (d, &term->lookup_color(fg, term->pix_colors).c, f, enc, ep - enc);
     }
 }
 
diff --git a/src/rxvtfont.h b/src/rxvtfont.h
index 40bdc7f..0e927f4 100644
--- a/src/rxvtfont.h
+++ b/src/rxvtfont.h
@@ -72,8 +72,10 @@ struct rxvt_fontset
   char *fontdesc;
 
   // must be power-of-two - 1, also has to match RS_fontMask in rxvt.h
-#if USE_256_COLORS
-  enum { fontCount =   7 }; // 2 extra colors bits, 2 fewer fontcount bits
+#if USE_24_BIT_COLOR
+  enum { fontCount =   7 }; // 36 extra colors bits, 2 fewer fontcount bits
+#elif USE_256_COLORS
+  enum { fontCount =   7 }; //  2 extra colors bits, 2 fewer fontcount bits
 #else
   enum { fontCount =  31 };
 #endif
diff --git a/src/rxvtperl.xs b/src/rxvtperl.xs
index ba697f0..2f7f84f 100644
--- a/src/rxvtperl.xs
+++ b/src/rxvtperl.xs
@@ -802,6 +802,18 @@ MODULE = urxvt             PACKAGE = urxvt
 
 PROTOTYPES: ENABLE
 
+TYPEMAP: <<END
+rend_t T_UINT
+
+INPUT
+T_UINT
+  $var = ($type)SvUV($arg);
+
+OUTPUT
+T_UINT
+  sv_setuv($arg, (UV)$var);
+END
+
 BOOT:
 {
   sv_setsv (get_sv ("urxvt::LIBDIR",   1), newSVpvn (LIBDIR,   sizeof (LIBDIR)   - 1));
@@ -1128,43 +1140,43 @@ NOW ()
         OUTPUT:
         RETVAL
 
-int
-GET_BASEFG (int rend)
+rend_t
+GET_BASEFG (rend_t rend)
 	CODE:
         RETVAL = GET_BASEFG (rend);
 	OUTPUT:
         RETVAL
 
-int
-GET_BASEBG (int rend)
+rend_t
+GET_BASEBG (rend_t rend)
 	CODE:
         RETVAL = GET_BASEBG (rend);
 	OUTPUT:
         RETVAL
 
-int
-SET_FGCOLOR (int rend, int new_color)
+rend_t
+SET_FGCOLOR (rend_t rend, int new_color)
 	CODE:
         RETVAL = SET_FGCOLOR (rend, clamp (new_color, 0, TOTAL_COLORS - 1));
 	OUTPUT:
         RETVAL
 
-int
-SET_BGCOLOR (int rend, int new_color)
+rend_t
+SET_BGCOLOR (rend_t rend, int new_color)
 	CODE:
         RETVAL = SET_BGCOLOR (rend, clamp (new_color, 0, TOTAL_COLORS - 1));
 	OUTPUT:
         RETVAL
 
-int
-GET_CUSTOM (int rend)
+rend_t
+GET_CUSTOM (rend_t rend)
 	CODE:
         RETVAL = (rend & RS_customMask) >> RS_customShift;
 	OUTPUT:
         RETVAL
 
-int
-SET_CUSTOM (int rend, int new_value)
+rend_t
+SET_CUSTOM (rend_t rend, int new_value)
 	CODE:
 {
         if (!IN_RANGE_EXC (new_value, 0, RS_customCount))
@@ -1619,8 +1631,8 @@ rxvt_term::vt_emask_add (U32 emask)
         THIS->vt_emask_perl |= emask;
         THIS->vt_select_input ();
 
-U32
-rxvt_term::rstyle (U32 new_rstyle = THIS->rstyle)
+UV
+rxvt_term::rstyle (rend_t new_rstyle = THIS->rstyle)
 	CODE:
         RETVAL = THIS->rstyle;
         THIS->rstyle = new_rstyle;
@@ -2106,10 +2118,10 @@ rxvt_term::cur_charset ()
         RETVAL
 
 void
-rxvt_term::scr_xor_rect (int beg_row, int beg_col, int end_row, int end_col, U32 rstyle1 = RS_RVid, U32 rstyle2 = RS_RVid | RS_Uline)
+rxvt_term::scr_xor_rect (int beg_row, int beg_col, int end_row, int end_col, rend_t rstyle1 = RS_RVid, rend_t rstyle2 = RS_RVid | RS_Uline)
 
 void
-rxvt_term::scr_xor_span (int beg_row, int beg_col, int end_row, int end_col, U32 rstyle = RS_RVid)
+rxvt_term::scr_xor_span (int beg_row, int beg_col, int end_row, int end_col, rend_t rstyle = RS_RVid)
 
 void
 rxvt_term::scr_bell ()
@@ -2175,7 +2187,7 @@ rxvt_term::cmd_parse (SV *octets)
 }
 
 SV *
-rxvt_term::overlay (int x, int y, int w, int h, int rstyle = OVERLAY_RSTYLE, int border = 2)
+rxvt_term::overlay (int x, int y, int w, int h, rend_t rstyle = OVERLAY_RSTYLE, int border = 2)
 	CODE:
 {
         overlay *o = new overlay (THIS, x, y, w, h, rstyle, border);
@@ -2297,8 +2309,8 @@ Window
 XCreateSimpleWindow (rxvt_term *term, Window parent, int x, int y, unsigned int width, unsigned int height)
 	C_ARGS: term->dpy, (Window)parent,
                 x, y, width, height, 0,
-                term->pix_colors_focused[Color_border],
-                term->pix_colors_focused[Color_border]
+                term->lookup_color(Color_border, term->pix_colors_focused),
+                term->lookup_color(Color_border, term->pix_colors_focused)
 
 #endif
 
@@ -2432,7 +2444,7 @@ rxvt_term::set_background (rxvt_img *img, bool border = false)
             img->reify ()
                ->replace (img);
 
-            img->convert_format (XRenderFindVisualFormat (THIS->dpy, THIS->visual), THIS->pix_colors [Color_bg])
+            img->convert_format (XRenderFindVisualFormat (THIS->dpy, THIS->visual), THIS->lookup_color(Color_bg, THIS->pix_colors))
                ->replace (img);
 
             THIS->bg_img = img;
diff --git a/src/screen.C b/src/screen.C
index 8fdfad9..fb3981e 100644
--- a/src/screen.C
+++ b/src/screen.C
@@ -627,12 +627,51 @@ rxvt_term::scr_color (unsigned int color, int fgbg) noexcept
     rstyle = SET_BGCOLOR (rstyle, color);
 }
 
+#if USE_24_BIT_COLOR
+static rxvt_color *scr_colors[1 << 24];
+
+void
+rxvt_term::scr_color_24 (unsigned int color, int fgbg) noexcept
+{
+  color += TOTAL_COLORS;
+  if (fgbg == Color_fg)
+    rstyle = SET_FGCOLOR (rstyle, color);
+  else
+    rstyle = SET_BGCOLOR (rstyle, color);
+}
+
+void
+rxvt_term::scr_color_rgb (unsigned int r, unsigned int g, unsigned int b, int fgbg) noexcept
+{
+  unsigned int color = (r << 16) + (g << 8) + b;
+  scr_color_24(color, fgbg);
+}
+#endif
+
+rxvt_color
+&rxvt_term::lookup_color (unsigned int color, rxvt_color *table) noexcept
+{
+#if USE_24_BIT_COLOR
+  if (color >= TOTAL_COLORS) {
+    color -= TOTAL_COLORS;
+    if (scr_colors[color] == NULL) {
+        scr_colors[color] = new rxvt_color();
+        char name[1+2*3+1];
+        sprintf(name, "#%02x%02x%02x", (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+        scr_colors[color]->set(this, name);
+    }
+    return *scr_colors[color];
+  } else
+#endif
+  return table[color];
+}
+
 /* ------------------------------------------------------------------------- */
 /*
  * Change the rendition style for following text
  */
 void
-rxvt_term::scr_rendition (int set, int style) noexcept
+rxvt_term::scr_rendition (int set, rend_t style) noexcept
 {
   if (set)
     rstyle |= style;
@@ -1390,13 +1429,13 @@ rxvt_term::scr_erase_screen (int mode) noexcept
 
       if (mapped)
         {
-          gcvalue.foreground = pix_colors[bgcolor_of (rstyle)];
+          gcvalue.foreground = lookup_color(bgcolor_of (rstyle), pix_colors);
           XChangeGC (dpy, gc, GCForeground, &gcvalue);
           XFillRectangle (dpy, vt, gc,
                           0, Row2Pixel (row - view_start),
                           (unsigned int)vt_width,
                           (unsigned int)Height2Pixel (num));
-          gcvalue.foreground = pix_colors[Color_fg];
+          gcvalue.foreground = lookup_color(Color_fg, pix_colors);
           XChangeGC (dpy, gc, GCForeground, &gcvalue);
         }
     }
@@ -1725,15 +1764,15 @@ rxvt_term::scr_rvideo_mode (bool on) noexcept
         }
       else
 #endif
-      ::swap (pix_colors[Color_fg], pix_colors[Color_bg]);
+      ::swap (lookup_color(Color_fg, pix_colors), lookup_color(Color_bg, pix_colors));
 #ifdef HAVE_IMG
       if (bg_img == 0)
 #endif
-          XSetWindowBackground (dpy, vt, pix_colors[Color_bg]);
+          XSetWindowBackground (dpy, vt, lookup_color(Color_bg, pix_colors));
 
       XGCValues gcvalue;
-      gcvalue.foreground = pix_colors[Color_fg];
-      gcvalue.background = pix_colors[Color_bg];
+      gcvalue.foreground = lookup_color(Color_fg, pix_colors);
+      gcvalue.background = lookup_color(Color_bg, pix_colors);
       XChangeGC (dpy, gc, GCBackground | GCForeground, &gcvalue);
 
       scr_clear ();
@@ -2431,14 +2470,14 @@ rxvt_term::scr_refresh () noexcept
             {
               if (showcursor && focus && row == screen.cur.row
                   && IN_RANGE_EXC (col, cur_col, cur_col + cursorwidth))
-                XSetForeground (dpy, gc, pix_colors[ccol1]);
+                XSetForeground (dpy, gc, lookup_color(ccol1, pix_colors));
               else
 #if ENABLE_FRILLS
               if (ISSET_PIXCOLOR (Color_underline))
-                XSetForeground (dpy, gc, pix_colors[Color_underline]);
+                XSetForeground (dpy, gc, lookup_color(Color_underline, pix_colors));
               else
 #endif
-                XSetForeground (dpy, gc, pix_colors[fore]);
+                XSetForeground (dpy, gc, lookup_color(fore, pix_colors));
 
               XDrawLine (dpy, vt, gc,
                          xpixel, ypixel + font->ascent + 1,
@@ -2458,7 +2497,7 @@ rxvt_term::scr_refresh () noexcept
             scr_set_char_rend (ROW(screen.cur.row), cur_col, cur_rend);
           else if (oldcursor.row >= 0)
             {
-              XSetForeground (dpy, gc, pix_colors[ccol1]);
+              XSetForeground (dpy, gc, lookup_color(ccol1, pix_colors));
               if (cursor_type == 1)
                 XFillRectangle (dpy, vt, gc,
                                 Col2Pixel (cur_col),
@@ -2475,7 +2514,7 @@ rxvt_term::scr_refresh () noexcept
         }
       else if (oldcursor.row >= 0)
         {
-          XSetForeground (dpy, gc, pix_colors[ccol1]);
+          XSetForeground (dpy, gc, lookup_color(ccol1, pix_colors));
 
           XDrawRectangle (dpy, vt, gc,
                           Col2Pixel (cur_col),
@@ -2544,15 +2583,15 @@ rxvt_term::scr_recolor (bool refresh) noexcept
       else
 # endif
         {
-          XSetWindowBackground (dpy, parent, pix_colors[Color_border]);
+          XSetWindowBackground (dpy, parent, lookup_color(Color_border, pix_colors));
           XSetWindowBackgroundPixmap (dpy, vt, bg_img->pm);
         }
     }
   else
 #endif
     {
-      XSetWindowBackground (dpy, parent, pix_colors[Color_border]);
-      XSetWindowBackground (dpy, vt, pix_colors[Color_bg]);
+      XSetWindowBackground (dpy, parent, lookup_color(Color_border, pix_colors));
+      XSetWindowBackground (dpy, vt, lookup_color(Color_bg, pix_colors));
     }
 
   XClearWindow (dpy, parent);
@@ -2562,7 +2601,7 @@ rxvt_term::scr_recolor (bool refresh) noexcept
       if (transparent)
         XSetWindowBackgroundPixmap (dpy, scrollBar.win, ParentRelative);
       else
-        XSetWindowBackground (dpy, scrollBar.win, pix_colors[scrollBar.color ()]);
+        XSetWindowBackground (dpy, scrollBar.win, lookup_color(scrollBar.color (), pix_colors));
       scrollBar.state = SB_STATE_IDLE;
       scrollBar.show (0);
     }
diff --git a/src/scrollbar-next.C b/src/scrollbar-next.C
index 2d9db7b..8c0679a 100644
--- a/src/scrollbar-next.C
+++ b/src/scrollbar-next.C
@@ -154,15 +154,15 @@ scrollBar_t::init_next ()
 
   gcvalue.graphics_exposures = False;
 
-  gcvalue.foreground = term->pix_colors_focused[Color_Black];
+  gcvalue.foreground = term->lookup_color(Color_Black, term->pix_colors_focused);
   blackGC = XCreateGC (term->dpy, win,
                        GCForeground | GCGraphicsExposures, &gcvalue);
 
-  gcvalue.foreground = term->pix_colors_focused[Color_White];
+  gcvalue.foreground = term->lookup_color(Color_White, term->pix_colors_focused);
   whiteGC = XCreateGC (term->dpy, win,
                        GCForeground | GCGraphicsExposures, &gcvalue);
 
-  light = term->pix_colors_focused[Color_scroll];
+  light = term->lookup_color(Color_scroll, term->pix_colors_focused);
 #if 0
   //color used by rxvt
   if (color.set (term, rgba (0xaeba, 0xaaaa, 0xaeba)))
@@ -172,7 +172,7 @@ scrollBar_t::init_next ()
   grayGC = XCreateGC (term->dpy, win,
                       GCForeground | GCGraphicsExposures, &gcvalue);
 
-  dark = term->pix_colors_focused[Color_Grey25];
+  dark = term->lookup_color(Color_Grey25, term->pix_colors_focused);
 #if 0
   //color used by rxvt
   if (color.set (term, rgba (0x51aa, 0x5555, 0x5144)))
diff --git a/src/scrollbar-plain.C b/src/scrollbar-plain.C
index cafc574..64b39e6 100644
--- a/src/scrollbar-plain.C
+++ b/src/scrollbar-plain.C
@@ -38,7 +38,7 @@ scrollBar_t::show_plain (int update)
       XGCValues gcvalue;
 
       init |= SB_STYLE_PLAIN;
-      gcvalue.foreground = term->pix_colors_focused[Color_scroll];
+      gcvalue.foreground = term->lookup_color(Color_scroll, term->pix_colors_focused);
 
       pscrollbarGC = XCreateGC (term->dpy, win, GCForeground, &gcvalue);
     }
diff --git a/src/scrollbar-rxvt.C b/src/scrollbar-rxvt.C
index 6359081..8bbde6e 100644
--- a/src/scrollbar-rxvt.C
+++ b/src/scrollbar-rxvt.C
@@ -158,11 +158,11 @@ scrollBar_t::show_rxvt (int update)
 
       init |= SB_STYLE_RXVT;
 
-      gcvalue.foreground = term->pix_colors[Color_topShadow];
+      gcvalue.foreground = term->lookup_color(Color_topShadow, term->pix_colors);
       topShadowGC = XCreateGC (term->dpy, term->vt, GCForeground, &gcvalue);
-      gcvalue.foreground = term->pix_colors[Color_bottomShadow];
+      gcvalue.foreground = term->lookup_color(Color_bottomShadow, term->pix_colors);
       botShadowGC = XCreateGC (term->dpy, term->vt, GCForeground, &gcvalue);
-      gcvalue.foreground = term->pix_colors[ (term->depth <= 2 ? Color_fg : Color_scroll)];
+      gcvalue.foreground = term->lookup_color( (term->depth <= 2 ? Color_fg : Color_scroll), term->pix_colors);
       scrollbarGC = XCreateGC (term->dpy, term->vt, GCForeground, &gcvalue);
     }
   else
diff --git a/src/scrollbar-xterm.C b/src/scrollbar-xterm.C
index e37fe20..7752e4f 100644
--- a/src/scrollbar-xterm.C
+++ b/src/scrollbar-xterm.C
@@ -49,13 +49,13 @@ scrollBar_t::show_xterm (int update)
         rxvt_fatal ("can't create bitmap\n");
 
       gcvalue.fill_style = FillOpaqueStippled;
-      gcvalue.foreground = term->pix_colors_focused[Color_scroll];
-      gcvalue.background = term->pix_colors_focused[Color_bg];
+      gcvalue.foreground = term->lookup_color(Color_scroll, term->pix_colors_focused);
+      gcvalue.background = term->lookup_color(Color_bg, term->pix_colors_focused);
 
       xscrollbarGC = XCreateGC (term->dpy, win,
                                 GCForeground | GCBackground
                                 | GCFillStyle | GCStipple, &gcvalue);
-      gcvalue.foreground = term->pix_colors_focused[Color_border];
+      gcvalue.foreground = term->lookup_color(Color_border, term->pix_colors_focused);
       ShadowGC = XCreateGC (term->dpy, win, GCForeground, &gcvalue);
     }
 
diff --git a/src/scrollbar.C b/src/scrollbar.C
index 698c582..c34e234 100644
--- a/src/scrollbar.C
+++ b/src/scrollbar.C
@@ -72,8 +72,8 @@ scrollBar_t::resize ()
                                  total_width (),
                                  term->szHint.height,
                                  0,
-                                 term->pix_colors[Color_fg],
-                                 term->pix_colors[color ()]);
+                                 term->lookup_color(Color_fg, term->pix_colors),
+                                 term->lookup_color(color (), term->pix_colors));
       XDefineCursor (term->dpy, win, leftptr_cursor);
 
       XSelectInput (term->dpy, win,
