From af0ef07c33e5d2db400033579e3878978ce6a55f Mon Sep 17 00:00:00 2001 From: Chris Johnsen Date: Wed, 5 Aug 2026 07:07:24 -0500 Subject: [PATCH 1/2] Screen::Pen: handle COLOR_RESET color values `COLOR_RESET` seems like it should not be applied to drawing into DF buffers. Callers should probably specify actual colors. For Pen colors coming from Lua: - intercept `COLOR_RESET` values and translate them to `COLOR_GREY` (fg) or `COLOR_BLACK` (bg), and - mask off all values to four bits to keep them in the 16 color range. Those grey and black "default" colors are the default color values provided by the Pen constructor. They also correspond to the colors used when `fg` and `bg` are nil coming from Lua. On the C++ side, the constructors and color-modifying member functions now mask off the color values to four bits, but do not do the fancier fg/bg-based defaulting. Since `Screen::Pen` is a struct with public data members, there is no good place to fully intercept changes to the color values. Add unit tests for the `fg`/`bg`/`tile_fg`/`tile_bg` handling of the Lua `dfhack.pen` interface to `Screen::Pen`. Most of the tests (those not in the last `COLOR_RESET` "section") document existing functionality and pass without the changes in this commit. Masking is not currently tested. Convert some "magic numbers" being used for Pen colors to their `color_value` enumerator names. --- library/LuaApi.cpp | 43 ++- library/include/modules/Screen.h | 18 +- library/modules/Screen.cpp | 10 +- test/library/pen.lua | 455 +++++++++++++++++++++++++++++++ 4 files changed, 499 insertions(+), 27 deletions(-) create mode 100644 test/library/pen.lua diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 6a08756156..c8f6277225 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -22,6 +22,7 @@ must not be misrepresented as being the original software. distribution. */ +#include "ColorText.h" #include "Core.h" #include "Error.h" #include "Internal.h" @@ -232,14 +233,30 @@ static bool get_bool_field(lua_State *L, bool *pf, int idx, const char *name, bo return !nil; } +static int8_t mask_pen_color(int8_t color, int8_t default_color) { + if (color == COLOR_RESET) + color = default_color; + return color & 15; +} + +static bool get_color_field(lua_State *L, int8_t *pf, int idx, const char *name, int defval) +{ + bool nonnil = get_int_field(L, pf, idx, name, defval); // defval for nil + *pf = mask_pen_color(*pf, defval); // defval for COLOR_RESET + return nonnil; +} + +static constexpr int8_t DEFAULT_FG = COLOR_GREY; +static constexpr int8_t DEFAULT_BG = COLOR_BLACK; + static void decode_pen(lua_State *L, Pen &pen, int idx) { idx = lua_absindex(L, idx); get_char_field(L, &pen.ch, idx, "ch", 0); - get_int_field(L, &pen.fg, idx, "fg", 7); - get_int_field(L, &pen.bg, idx, "bg", 0); + get_color_field(L, &pen.fg, idx, "fg", DEFAULT_FG); + get_color_field(L, &pen.bg, idx, "bg", DEFAULT_BG); lua_getfield(L, idx, "bold"); if (lua_isnil(L, -1)) @@ -252,8 +269,8 @@ static void decode_pen(lua_State *L, Pen &pen, int idx) get_int_or_closure_field(L, &pen.tile, idx, "tile", 0); - bool tcolor = get_int_field(L, &pen.tile_fg, idx, "tile_fg", 7); - tcolor = get_int_field(L, &pen.tile_bg, idx, "tile_bg", 0) || tcolor; + bool tcolor = get_color_field(L, &pen.tile_fg, idx, "tile_fg", DEFAULT_FG); + tcolor = get_color_field(L, &pen.tile_bg, idx, "tile_bg", DEFAULT_BG) || tcolor; if (tcolor) pen.tile_mode = Pen::TileColor; @@ -672,7 +689,7 @@ void Lua::CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil, bo } else if (allow_color && lua_isnumber(L, index)) { - *pen = Pen(0, lua_tointeger(L, index)&15, 0); + *pen = Pen(0, mask_pen_color(lua_tointeger(L, index), DEFAULT_FG), 0); } else { @@ -698,8 +715,8 @@ static int adjust_pen(lua_State *L, bool no_copy) iidx = -1; - pen.fg = luaL_optint(L, 2, pen.fg) & 15; - pen.bg = luaL_optint(L, 3, pen.bg); + pen.fg = mask_pen_color(luaL_optint(L, 2, pen.fg), DEFAULT_FG); + pen.bg = mask_pen_color(luaL_optint(L, 3, pen.bg), DEFAULT_BG); if (!lua_isnil(L, 4)) pen.bold = lua_toboolean(L, 4); @@ -864,7 +881,7 @@ static int dfhack_pen_newindex(lua_State *L) lua_pushinteger(L, (unsigned char)pen.ch); break; case 1: - pen.fg = luaL_checkint(L, 3) & 15; + pen.fg = mask_pen_color(luaL_checkint(L, 3), DEFAULT_FG); lua_pushinteger(L, pen.fg); break; case 2: @@ -872,7 +889,7 @@ static int dfhack_pen_newindex(lua_State *L) lua_pushboolean(L, pen.bold); break; case 3: - pen.bg = luaL_checkint(L, 3) & 15; + pen.bg = mask_pen_color(luaL_checkint(L, 3), DEFAULT_BG); lua_pushinteger(L, pen.bg); break; case 4: @@ -891,14 +908,14 @@ static int dfhack_pen_newindex(lua_State *L) lua_pushboolean(L, pen.tile_mode == Pen::CharColor); break; case 6: - if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_bg = 0; } - pen.tile_fg = luaL_checkint(L, 3) & 15; + if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_bg = DEFAULT_BG; } + pen.tile_fg = mask_pen_color(luaL_checkint(L, 3), DEFAULT_FG); pen.tile_mode = Pen::TileColor; lua_pushinteger(L, pen.tile_fg); break; case 7: - if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_fg = 7; } - pen.tile_bg = luaL_checkint(L, 3) & 15; + if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_fg = DEFAULT_FG; } + pen.tile_bg = mask_pen_color(luaL_checkint(L, 3), DEFAULT_BG); pen.tile_mode = Pen::TileColor; lua_pushinteger(L, pen.tile_bg); break; diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 173cda6aa8..86c619c2b5 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -94,26 +94,26 @@ namespace DFHack // NOTE: LuaApi.cpp assumes this struct is plain data and has empty destructor Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) - : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), + : ch(ch), fg(fg&7), bg(bg&15), bold(!!(fg&8)), tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) {} Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile = 0, bool color_tile = false) - : ch(ch), fg(fg), bg(bg), bold(bold), + : ch(ch), fg(fg&15), bg(bg&15), bold(bold), tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) {} Pen(char ch, int8_t fg, int8_t bg, int tile, int8_t tile_fg, int8_t tile_bg) - : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), - tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg) + : ch(ch), fg(fg&7), bg(bg&15), bold(!!(fg&8)), + tile(tile), tile_mode(TileColor), tile_fg(tile_fg&15), tile_bg(tile_bg&15) {} Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile, int8_t tile_fg, int8_t tile_bg) - : ch(ch), fg(fg), bg(bg), bold(bold), - tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg) + : ch(ch), fg(fg&15), bg(bg&15), bold(bold), + tile(tile), tile_mode(TileColor), tile_fg(tile_fg&15), tile_bg(tile_bg&15) {} void adjust(int8_t nfg) { fg = nfg&7; bold = !!(nfg&8); } - void adjust(int8_t nfg, bool nbold) { fg = nfg; bold = nbold; } - void adjust(int8_t nfg, int8_t nbg) { adjust(nfg); bg = nbg; } - void adjust(int8_t nfg, bool nbold, int8_t nbg) { adjust(nfg, nbold); bg = nbg; } + void adjust(int8_t nfg, bool nbold) { fg = nfg&15; bold = nbold; } + void adjust(int8_t nfg, int8_t nbg) { adjust(nfg); bg = nbg&15; } + void adjust(int8_t nfg, bool nbold, int8_t nbg) { adjust(nfg, nbold); bg = nbg&15; } Pen color(int8_t nfg) const { Pen cp(*this); cp.adjust(nfg); return cp; } Pen color(int8_t nfg, bool nbold) const { Pen cp(*this); cp.adjust(nfg, nbold); return cp; } diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 338a66925e..7f2c284fee 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -477,9 +477,9 @@ bool Screen::drawBorder(const std::string &title) if (!gps) return false; auto dim = getWindowSize(); - Pen border('\xDB', 8); - Pen text(0, 0, 7); - Pen signature(0, 0, 8); + Pen border('\xDB', COLOR_DARKGREY); + Pen text(0, COLOR_BLACK, COLOR_GREY); + Pen signature(0, COLOR_BLACK, COLOR_DARKGREY); for (int x = 0; x < dim.x; x++) { @@ -513,8 +513,8 @@ bool Screen::invalidate() return true; } -const Pen Screen::Painter::default_pen(0,COLOR_GREY,0); -const Pen Screen::Painter::default_key_pen(0,COLOR_LIGHTGREEN,0); +const Pen Screen::Painter::default_pen(0, COLOR_GREY, COLOR_BLACK); +const Pen Screen::Painter::default_key_pen(0, COLOR_LIGHTGREEN, COLOR_BLACK); void Screen::Painter::do_paint_string(const std::string &str, const Pen &pen, bool map) { diff --git a/test/library/pen.lua b/test/library/pen.lua new file mode 100644 index 0000000000..c37cf55118 --- /dev/null +++ b/test/library/pen.lua @@ -0,0 +1,455 @@ +config.target = 'core' + +-- nil "base" pen + +function test.make_base_nil() + local pen = dfhack.pen.make(nil) + expect.nil_(pen) +end + +function test.make_base_empty() + local pen = dfhack.pen.make{} + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_base_nil_fg() + local pen = dfhack.pen.make(nil, COLOR_RED) + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + pen = dfhack.pen.make(nil, COLOR_LIGHTRED) + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_base_fg_nil_bg() + local pen = dfhack.pen.make(nil, nil, COLOR_BLUE) + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.make_base_fg_bg_nil_bold() + local pen = dfhack.pen.make(nil, nil, nil, true) + expect.eq(pen.fg, COLOR_GREY) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + pen = dfhack.pen.make(nil, nil, nil, false) + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +-- color number "base" pen + +function test.make_base_color() + local pen = dfhack.pen.make(COLOR_RED) + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + pen = dfhack.pen.make(COLOR_LIGHTRED) + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_base_color_fg() + local pen = dfhack.pen.make(COLOR_RED, COLOR_GREEN) + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + pen = dfhack.pen.make(COLOR_RED, COLOR_LIGHTGREEN) + expect.eq(pen.fg, COLOR_GREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_base_color_fg_bg() + local pen = dfhack.pen.make(COLOR_RED, COLOR_GREEN, COLOR_BLUE) + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + + pen = dfhack.pen.make(COLOR_RED, COLOR_LIGHTGREEN, COLOR_BLUE) + expect.eq(pen.fg, COLOR_GREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.make_base_color_fg_bg_bold() + local pen = dfhack.pen.make(COLOR_RED, COLOR_GREEN, COLOR_BLUE, true) + expect.eq(pen.fg, COLOR_GREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + + -- maybe a bit unexpected? bold=true does not mask off "light" bit in fg + pen = dfhack.pen.make(COLOR_RED, COLOR_LIGHTGREEN, COLOR_BLUE, true) + expect.eq(pen.fg, COLOR_LIGHTGREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.make_base_color_fg_bg_nonbold() + local pen = dfhack.pen.make(COLOR_RED, COLOR_GREEN, COLOR_BLUE, false) + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + + -- maybe a bit unexpected? bold=false does not mask off "light" bit in fg + pen = dfhack.pen.make(COLOR_RED, COLOR_LIGHTGREEN, COLOR_BLUE, false) + expect.eq(pen.fg, COLOR_LIGHTGREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +-- table "base" pen + +function test.make_base_table_fg() + local pen = dfhack.pen.make{ fg = COLOR_RED } + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.false_(pen.tile_color) + + pen = dfhack.pen.make{ fg = COLOR_LIGHTRED } + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.false_(pen.tile_color) + + pen = dfhack.pen.make{ fg = COLOR_LIGHTRED, tile_color = true } + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.true_(pen.tile_color) +end + +function test.make_base_table_bg() + local pen = dfhack.pen.make{ bg = COLOR_BLUE } + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.false_(pen.tile_color) + + pen = dfhack.pen.make{ bg = COLOR_LIGHTBLUE, tile_color = true } + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_LIGHTBLUE) + expect.true_(pen.tile_color) +end + +function test.make_base_table_fg_bold() + local pen = dfhack.pen.make{ fg = COLOR_RED, bold = true } + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + -- maybe a bit unexpected? bold=true does not mask off "light" bit in fg + pen = dfhack.pen.make{ fg = COLOR_LIGHTRED, bold = true } + expect.eq(pen.fg, COLOR_LIGHTRED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_base_table_fg_nonbold() + local pen = dfhack.pen.make{ fg = COLOR_RED, bold = false } + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + + -- maybe a bit unexpected? bold=false does not mask off "light" bit in fg + pen = dfhack.pen.make{ fg = COLOR_LIGHTRED, bold = false } + expect.eq(pen.fg, COLOR_LIGHTRED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) +end + +-- first pen is ignored if second is a table or a Pen + +function test.make_base_table_pen() + local pen = dfhack.pen.make({ fg = COLOR_LIGHTRED, bg = COLOR_BLUE }, { fg = COLOR_GREEN, bg = COLOR_BROWN }) + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BROWN) + + local pen2 = dfhack.pen.make{ fg = COLOR_GREEN, bg = COLOR_BROWN } + pen = dfhack.pen.make({ fg = COLOR_LIGHTRED, bg = COLOR_BLUE }, pen2) + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BROWN) +end + +function test.make_base_pen_pen() + local base = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + local pen_table = { fg = COLOR_LIGHTGREEN, bg = COLOR_BROWN } + local pen = dfhack.pen.make(base, pen_table) + -- base is unchanged + expect.eq(base.fg, COLOR_RED) + expect.false_(base.bold) + expect.eq(base.bg, COLOR_BLUE) + -- pen has new values + expect.eq(pen.fg, COLOR_GREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BROWN) + + pen = dfhack.pen.make(base, dfhack.pen.make(pen_table)) + expect.eq(pen.fg, COLOR_GREEN) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BROWN) +end + +-- tile_fg, tile_bg + +function test.make_base_table_tile_fg() + local pen = dfhack.pen.make{ tile_fg = COLOR_RED } + expect.eq(pen.tile_fg, COLOR_RED) + expect.eq(pen.tile_bg, COLOR_BLACK) + expect.nil_(pen.tile_color) +end + +function test.make_base_table_tile_bg() + local pen = dfhack.pen.make{ tile_bg = COLOR_BLUE } + expect.eq(pen.tile_fg, COLOR_GREY) + expect.eq(pen.tile_bg, COLOR_BLUE) + expect.nil_(pen.tile_color) +end + +-- assign fields + +function test.assign_fg() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + pen.fg = COLOR_GREEN + expect.eq(pen.fg, COLOR_GREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + + -- assigning a "light" fg does not change bold + pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + pen.fg = COLOR_LIGHTGREEN + expect.eq(pen.fg, COLOR_LIGHTGREEN) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.assign_bg() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + pen.bg = COLOR_GREEN + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_GREEN) +end + +function test.assign_bold() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + pen.bold = true + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + + pen = dfhack.pen.make{ fg = COLOR_LIGHTRED, bg = COLOR_BLUE } + expect.eq(pen.fg, COLOR_RED) + expect.true_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + pen.bold = false + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.assign_tile_fg() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE, tile_color = true } + pen.tile_fg = COLOR_GREEN + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.eq(pen.tile_fg, COLOR_GREEN) + expect.eq(pen.tile_bg, COLOR_BLACK) + expect.nil_(pen.tile_color) + + pen = dfhack.pen.make{ tile_fg = COLOR_RED, tile_bg = COLOR_BLUE, tile_color = true } + pen.tile_fg = COLOR_GREEN + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.eq(pen.tile_fg, COLOR_GREEN) + expect.eq(pen.tile_bg, COLOR_BLUE) + expect.nil_(pen.tile_color) +end + +function test.assign_tile_bg() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE, tile_color = true } + pen.tile_bg = COLOR_GREEN + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.eq(pen.tile_fg, COLOR_GREY) + expect.eq(pen.tile_bg, COLOR_GREEN) + expect.nil_(pen.tile_color) + + pen = dfhack.pen.make{ tile_fg = COLOR_RED, tile_bg = COLOR_BLUE, tile_color = true } + pen.tile_bg = COLOR_GREEN + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.eq(pen.tile_fg, COLOR_RED) + expect.eq(pen.tile_bg, COLOR_GREEN) + expect.nil_(pen.tile_color) +end + +function test.assign_tile_color() + local tile = { tile_fg = COLOR_RED, tile_bg = COLOR_BLUE } + local pen = dfhack.pen.make(tile) + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.eq(pen.tile_fg, COLOR_RED) + expect.eq(pen.tile_bg, COLOR_BLUE) + expect.nil_(pen.tile_color) + + pen.tile_color = true + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.true_(pen.tile_color) + + + pen = dfhack.pen.make(tile) + pen.tile_color = false + expect.eq(pen.fg, COLOR_GREY) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLACK) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.false_(pen.tile_color) + + + pen = dfhack.pen.make({ fg = COLOR_RED, bg = COLOR_BLUE, tile_color = true }) + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.true_(pen.tile_color) + + pen.tile_color = false + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.false_(pen.tile_color) + + + pen = dfhack.pen.make({ fg = COLOR_RED, bg = COLOR_BLUE, tile_color = false }) + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.false_(pen.tile_color) + + pen.tile_color = true + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.true_(pen.tile_color) + + + -- a bit odd w.r.t usual Lua semantics: assigning nil to tile_color works like false + pen = dfhack.pen.make({ fg = COLOR_RED, bg = COLOR_BLUE, tile_color = true }) + pen.tile_color = nil + expect.eq(pen.fg, COLOR_RED) + expect.false_(pen.bold) + expect.eq(pen.bg, COLOR_BLUE) + expect.nil_(pen.tile_fg) + expect.nil_(pen.tile_bg) + expect.false_(pen.tile_color) +end + +-- COLOR_RESET should translate to "default" colors + +function test.make_base_reset() + local pen = dfhack.pen.make(COLOR_RESET) + expect.eq(pen.fg, COLOR_GREY) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_pen_reset() + local base = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + local pen = dfhack.pen.make(base, COLOR_RESET) + expect.eq(pen.fg, COLOR_GREY) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.make_bg_reset() + local base = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + local pen = dfhack.pen.make(base, nil, COLOR_RESET) + expect.eq(pen.fg, COLOR_RED) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_pen_table_fg_reset() + local pen = dfhack.pen.make{ fg = COLOR_RESET } + expect.eq(pen.fg, COLOR_GREY) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_pen_table_bg_reset() + local pen = dfhack.pen.make{ bg = COLOR_RESET } + expect.eq(pen.fg, COLOR_GREY) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.assign_fg_reset() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + pen.fg = COLOR_RESET + expect.eq(pen.fg, COLOR_GREY) + expect.eq(pen.bg, COLOR_BLUE) +end + +function test.assign_bg_reset() + local pen = dfhack.pen.make{ fg = COLOR_RED, bg = COLOR_BLUE } + pen.bg = COLOR_RESET + expect.eq(pen.fg, COLOR_RED) + expect.eq(pen.bg, COLOR_BLACK) +end + +function test.make_pen_table_tile_fg_reset() + local pen = dfhack.pen.make{ tile_fg = COLOR_RESET } + expect.eq(pen.tile_fg, COLOR_GREY) + expect.eq(pen.tile_bg, COLOR_BLACK) +end + +function test.make_pen_table_tile_bg_reset() + local pen = dfhack.pen.make{ tile_bg = COLOR_RESET } + expect.eq(pen.tile_fg, COLOR_GREY) + expect.eq(pen.tile_bg, COLOR_BLACK) +end + +function test.assign_tile_fg_reset() + local pen = dfhack.pen.make{ tile_fg = COLOR_RED, tile_bg = COLOR_BLUE } + pen.tile_fg = COLOR_RESET + expect.eq(pen.tile_fg, COLOR_GREY) + expect.eq(pen.tile_bg, COLOR_BLUE) +end + +function test.assign_tile_bg_reset() + local pen = dfhack.pen.make{ tile_fg = COLOR_RED, tile_bg = COLOR_BLUE } + pen.tile_bg = COLOR_RESET + expect.eq(pen.tile_fg, COLOR_RED) + expect.eq(pen.tile_bg, COLOR_BLACK) +end From dc37db3a757f649ebbdcab7e481aba77523d3271 Mon Sep 17 00:00:00 2001 From: Chris Johnsen Date: Wed, 5 Aug 2026 07:15:24 -0500 Subject: [PATCH 2/2] changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9ad9dd18dd..da852776ff 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -61,6 +61,7 @@ Template for new versions: ## Fixes - a safety check was added to ``Screen::doSetTile_char`` fur out of bound pen color values - ``TextArea`` widget corrected to use ``COLOR_BLACK`` instead of ``COLOR_RESET`` as default background color +- ``Screen::Pen``: COLOR_RESET from Lua is changed to grey (fg) or black (bg); color values are masked to 4 bits to keep them in the expected range ## Misc Improvements