Add a native pen object for lua with a more checked behavior.

develop
Alexander Gavrilov 2012-11-03 20:06:33 +04:00
parent d6f1bb93b5
commit 9598316855
7 changed files with 466 additions and 115 deletions

@ -1555,37 +1555,12 @@ be feasibly used in the core context.</p>
<p>Checks if [GRAPHICS:YES] was specified in init.</p> <p>Checks if [GRAPHICS:YES] was specified in init.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.screen.paintTile(pen,x,y[,char,tile])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.screen.paintTile(pen,x,y[,char,tile])</span></tt></p>
<p>Paints a tile using given parameters. Pen is a table with following possible fields:</p> <p>Paints a tile using given parameters. See below for a description of pen.</p>
<dl class="docutils">
<dt><tt class="docutils literal">ch</tt></dt>
<dd><p class="first last">Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the <tt class="docutils literal">char</tt> function parameter.</p>
</dd>
<dt><tt class="docutils literal">fg</tt></dt>
<dd><p class="first last">Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).</p>
</dd>
<dt><tt class="docutils literal">bg</tt></dt>
<dd><p class="first last">Background color for the ordinary tile. Defaults to COLOR_BLACK (0).</p>
</dd>
<dt><tt class="docutils literal">bold</tt></dt>
<dd><p class="first last">Bright/bold text flag. If <em>nil</em>, computed based on (fg &amp; 8); fg is masked to 3 bits.
Otherwise should be <em>true/false</em>.</p>
</dd>
<dt><tt class="docutils literal">tile</tt></dt>
<dd><p class="first last">Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.</p>
</dd>
<dt><tt class="docutils literal">tile_color = true</tt></dt>
<dd><p class="first last">Specifies that the tile should be shaded with <em>fg/bg</em>.</p>
</dd>
<dt><tt class="docutils literal">tile_fg, tile_bg</tt></dt>
<dd><p class="first last">If specified, overrides <em>tile_color</em> and supplies shading colors directly.</p>
</dd>
</dl>
<p>Returns <em>false</em> if coordinates out of bounds, or other error.</p> <p>Returns <em>false</em> if coordinates out of bounds, or other error.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.screen.readTile(x,y)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.screen.readTile(x,y)</tt></p>
<p>Retrieves the contents of the specified tile from the screen buffers. <p>Retrieves the contents of the specified tile from the screen buffers.
Returns a pen, or <em>nil</em> if invalid or TrueType.</p> Returns a pen object, or <em>nil</em> if invalid or TrueType.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.screen.paintString(pen,x,y,text)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.screen.paintString(pen,x,y,text)</tt></p>
<p>Paints the string starting at <em>x,y</em>. Uses the string characters <p>Paints the string starting at <em>x,y</em>. Uses the string characters
@ -1610,6 +1585,59 @@ The values can then be used for the <em>tile</em> field of <em>pen</em> structur
functions in this section, this may be used at any time.</p> functions in this section, this may be used at any time.</p>
</li> </li>
</ul> </ul>
<p>The &quot;pen&quot; argument used by functions above may be represented by
a table with the following possible fields:</p>
<blockquote>
<dl class="docutils">
<dt><tt class="docutils literal">ch</tt></dt>
<dd>Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the <tt class="docutils literal">char</tt> function parameter.</dd>
<dt><tt class="docutils literal">fg</tt></dt>
<dd>Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).</dd>
<dt><tt class="docutils literal">bg</tt></dt>
<dd>Background color for the ordinary tile. Defaults to COLOR_BLACK (0).</dd>
<dt><tt class="docutils literal">bold</tt></dt>
<dd>Bright/bold text flag. If <em>nil</em>, computed based on (fg &amp; 8); fg is masked to 3 bits.
Otherwise should be <em>true/false</em>.</dd>
<dt><tt class="docutils literal">tile</tt></dt>
<dd>Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.</dd>
<dt><tt class="docutils literal">tile_color = true</tt></dt>
<dd>Specifies that the tile should be shaded with <em>fg/bg</em>.</dd>
<dt><tt class="docutils literal">tile_fg, tile_bg</tt></dt>
<dd>If specified, overrides <em>tile_color</em> and supplies shading colors directly.</dd>
</dl>
</blockquote>
<p>Alternatively, it may be a pre-parsed native object with the following API:</p>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.pen.make(base[,pen_or_fg,bg,bold])</span></tt></p>
<p>Creates a new pre-parsed pen by combining its arguments according to the
following rules:</p>
<ol class="arabic simple">
<li>The <tt class="docutils literal">base</tt> argument may be a pen object, a pen table as specified above,
or a single color value. In the single value case, it is split into
<tt class="docutils literal">fg</tt> and <tt class="docutils literal">bold</tt> properties, and others are initialized to 0.
This argument will be converted to a pre-parsed object and returned
if there are no other arguments.</li>
<li>If the <tt class="docutils literal">pen_or_fg</tt> argument is specified as a table or object, it
completely replaces the base, and is returned instead of it.</li>
<li>Otherwise, the non-nil subset of the optional arguments is used
to update the <tt class="docutils literal">fg</tt>, <tt class="docutils literal">bg</tt> and <tt class="docutils literal">bold</tt> properties of the base.
If the <tt class="docutils literal">bold</tt> flag is <em>nil</em>, but <em>pen_or_fg</em> is a number, <tt class="docutils literal">bold</tt>
is deduced from it like in the simple base case.</li>
</ol>
<p>This function always returns a new pre-parsed pen, or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.pen.parse(base[,pen_or_fg,bg,bold])</span></tt></p>
<p>Exactly like the above function, but returns <tt class="docutils literal">base</tt> or <tt class="docutils literal">pen_or_fg</tt>
directly if they are already a pre-parsed native object.</p>
</li>
<li><p class="first"><tt class="docutils literal">pen.property</tt>, <tt class="docutils literal">pen.property = value</tt>, <tt class="docutils literal">pairs(pen)</tt></p>
<p>Pre-parsed pens support reading and setting their properties,
but don't behave exactly like a simple table would; for instance,
assigning to <tt class="docutils literal">pen.tile_color</tt> also resets <tt class="docutils literal">pen.tile_fg</tt> and
<tt class="docutils literal">pen.tile_bg</tt> to <em>nil</em>.</p>
</li>
</ul>
<p>In order to actually be able to paint to the screen, it is necessary <p>In order to actually be able to paint to the screen, it is necessary
to create and register a viewscreen (basically a modal dialog) with to create and register a viewscreen (basically a modal dialog) with
the game.</p> the game.</p>

@ -1403,31 +1403,14 @@ Basic painting functions:
* ``dfhack.screen.paintTile(pen,x,y[,char,tile])`` * ``dfhack.screen.paintTile(pen,x,y[,char,tile])``
Paints a tile using given parameters. Pen is a table with following possible fields: Paints a tile using given parameters. See below for a description of pen.
``ch``
Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the ``char`` function parameter.
``fg``
Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).
``bg``
Background color for the ordinary tile. Defaults to COLOR_BLACK (0).
``bold``
Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits.
Otherwise should be *true/false*.
``tile``
Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.
``tile_color = true``
Specifies that the tile should be shaded with *fg/bg*.
``tile_fg, tile_bg``
If specified, overrides *tile_color* and supplies shading colors directly.
Returns *false* if coordinates out of bounds, or other error. Returns *false* if coordinates out of bounds, or other error.
* ``dfhack.screen.readTile(x,y)`` * ``dfhack.screen.readTile(x,y)``
Retrieves the contents of the specified tile from the screen buffers. Retrieves the contents of the specified tile from the screen buffers.
Returns a pen, or *nil* if invalid or TrueType. Returns a pen object, or *nil* if invalid or TrueType.
* ``dfhack.screen.paintString(pen,x,y,text)`` * ``dfhack.screen.paintString(pen,x,y,text)``
@ -1458,6 +1441,61 @@ Basic painting functions:
Requests repaint of the screen by setting a flag. Unlike other Requests repaint of the screen by setting a flag. Unlike other
functions in this section, this may be used at any time. functions in this section, this may be used at any time.
The "pen" argument used by functions above may be represented by
a table with the following possible fields:
``ch``
Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the ``char`` function parameter.
``fg``
Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).
``bg``
Background color for the ordinary tile. Defaults to COLOR_BLACK (0).
``bold``
Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits.
Otherwise should be *true/false*.
``tile``
Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.
``tile_color = true``
Specifies that the tile should be shaded with *fg/bg*.
``tile_fg, tile_bg``
If specified, overrides *tile_color* and supplies shading colors directly.
Alternatively, it may be a pre-parsed native object with the following API:
* ``dfhack.pen.make(base[,pen_or_fg,bg,bold])``
Creates a new pre-parsed pen by combining its arguments according to the
following rules:
1. The ``base`` argument may be a pen object, a pen table as specified above,
or a single color value. In the single value case, it is split into
``fg`` and ``bold`` properties, and others are initialized to 0.
This argument will be converted to a pre-parsed object and returned
if there are no other arguments.
2. If the ``pen_or_fg`` argument is specified as a table or object, it
completely replaces the base, and is returned instead of it.
3. Otherwise, the non-nil subset of the optional arguments is used
to update the ``fg``, ``bg`` and ``bold`` properties of the base.
If the ``bold`` flag is *nil*, but *pen_or_fg* is a number, ``bold``
is deduced from it like in the simple base case.
This function always returns a new pre-parsed pen, or *nil*.
* ``dfhack.pen.parse(base[,pen_or_fg,bg,bold])``
Exactly like the above function, but returns ``base`` or ``pen_or_fg``
directly if they are already a pre-parsed native object.
* ``pen.property``, ``pen.property = value``, ``pairs(pen)``
Pre-parsed pens support reading and setting their properties,
but don't behave exactly like a simple table would; for instance,
assigning to ``pen.tile_color`` also resets ``pen.tile_fg`` and
``pen.tile_bg`` to *nil*.
In order to actually be able to paint to the screen, it is necessary In order to actually be able to paint to the screen, it is necessary
to create and register a viewscreen (basically a modal dialog) with to create and register a viewscreen (basically a modal dialog) with
the game. the game.

@ -725,6 +725,316 @@ static void OpenMatinfo(lua_State *state)
lua_pop(state, 1); lua_pop(state, 1);
} }
/**************
* Pen object *
**************/
static int DFHACK_PEN_TOKEN = 0;
void Lua::Push(lua_State *L, const Screen::Pen &info)
{
if (!info.valid())
{
lua_pushnil(L);
return;
}
void *pdata = lua_newuserdata(L, sizeof(Pen));
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
lua_setmetatable(L, -2);
new (pdata) Pen(info);
}
static Pen *check_pen_native(lua_State *L, int index)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2))
luaL_argerror(L, index, "not a pen object");
lua_pop(L, 2);
return (Pen*)lua_touserdata(L, index);
}
void Lua::CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil, bool allow_color)
{
index = lua_absindex(L, index);
luaL_checkany(L, index);
if (lua_isnil(L, index))
{
if (!allow_nil)
luaL_argerror(L, index, "nil pen not allowed");
*pen = Pen(0,0,0,-1);
}
else if (lua_isuserdata(L, index))
{
*pen = *check_pen_native(L, index);
}
else if (allow_color && lua_isnumber(L, index))
{
*pen = Pen(0, lua_tointeger(L, index)&15, 0);
}
else
{
luaL_checktype(L, index, LUA_TTABLE);
decode_pen(L, *pen, index);
}
}
static int adjust_pen(lua_State *L, bool no_copy)
{
lua_settop(L, 4);
Pen pen;
int iidx = 1;
Lua::CheckPen(L, &pen, 1, true, true);
if (!lua_isnil(L, 2) || !lua_isnil(L, 3) || !lua_isnil(L, 4))
{
if (lua_isnumber(L, 2) || lua_isnil(L, 2))
{
if (!pen.valid())
pen = Pen();
iidx = -1;
pen.fg = luaL_optint(L, 2, pen.fg) & 15;
pen.bg = luaL_optint(L, 3, pen.bg);
if (!lua_isnil(L, 4))
pen.bold = lua_toboolean(L, 4);
else if (!lua_isnil(L, 2))
{
pen.bold = !!(pen.fg & 8);
pen.fg &= 7;
}
}
else
{
iidx = 2;
Lua::CheckPen(L, &pen, 2, false, false);
}
}
if (no_copy && iidx > 0 && lua_isuserdata(L, iidx))
lua_pushvalue(L, iidx);
else
Lua::Push(L, pen);
return 1;
}
static int dfhack_pen_parse(lua_State *L)
{
return adjust_pen(L, true);
}
static int dfhack_pen_make(lua_State *L)
{
return adjust_pen(L, false);
}
static void make_pen_table(lua_State *L, Pen &pen)
{
if (!pen.valid())
luaL_error(L, "invalid pen state");
else
{
lua_newtable(L);
lua_pushinteger(L, (unsigned char)pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
}
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
lua_pushboolean(L, false); lua_setfield(L, -2, "tile_color");
break;
}
}
}
static void get_pen_mirror(lua_State *L, int idx)
{
lua_getuservalue(L, idx);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
Pen pen;
Lua::CheckPen(L, &pen, idx, false, false);
make_pen_table(L, pen);
lua_dup(L);
lua_setuservalue(L, idx);
}
}
static int dfhack_pen_index(lua_State *L)
{
lua_settop(L, 2);
luaL_checktype(L, 1, LUA_TUSERDATA);
// check metatable
if (!lua_getmetatable(L, 1))
luaL_argerror(L, 1, "must be a pen");
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1))
return 1;
// otherwise read from the mirror table, creating it if necessary
lua_settop(L, 2);
get_pen_mirror(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int pen_pnext(lua_State *L)
{
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, lua_upvalueindex(1)))
return 2;
lua_pushnil(L);
return 1;
}
static int dfhack_pen_pairs(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
get_pen_mirror(L, 1);
lua_pushcclosure(L, pen_pnext, 1);
lua_pushnil(L);
lua_pushnil(L);
return 3;
}
const char *const pen_fields[] = {
"ch", "fg", "bold", "bg", "tile", "tile_color", "tile_fg", "tile_bg", NULL
};
static int dfhack_pen_newindex(lua_State *L)
{
lua_settop(L, 3);
luaL_checktype(L, 1, LUA_TUSERDATA);
int id = luaL_checkoption(L, 2, NULL, pen_fields);
int arg = 0;
Pen &pen = *check_pen_native(L, 1);
bool wipe_tile = false, wipe_tc = false;
switch (id) {
case 0:
if (lua_type(L, 3) != LUA_TNUMBER)
arg = (unsigned char)*luaL_checkstring(L, 3);
else
arg = luaL_checkint(L, 3);
pen.ch = arg;
lua_pushinteger(L, (unsigned char)pen.ch);
break;
case 1:
pen.fg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.fg);
break;
case 2:
pen.bold = lua_toboolean(L, 3);
lua_pushboolean(L, pen.bold);
break;
case 3:
pen.bg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.bg);
break;
case 4:
arg = lua_isnil(L, 3) ? 0 : luaL_checkint(L, 3);
if (arg < 0)
luaL_argerror(L, 3, "invalid tile index");
pen.tile = arg;
if (pen.tile)
lua_pushinteger(L, pen.tile);
else
lua_pushnil(L);
break;
case 5:
wipe_tile = (pen.tile_mode == Pen::TileColor);
pen.tile_mode = lua_toboolean(L, 3) ? Pen::CharColor : Pen::AsIs;
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;
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;
pen.tile_mode = Pen::TileColor;
lua_pushinteger(L, pen.tile_bg);
break;
}
lua_getuservalue(L, 1);
if (!lua_isnil(L, -1))
{
lua_remove(L, 3);
lua_insert(L, 2);
lua_rawset(L, 2);
if (wipe_tc) {
lua_pushnil(L); lua_setfield(L, 2, "tile_color");
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, 2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, 2, "tile_bg");
}
if (wipe_tile) {
lua_pushnil(L); lua_setfield(L, 2, "tile_fg");
lua_pushnil(L); lua_setfield(L, 2, "tile_bg");
}
}
return 0;
}
static const luaL_Reg dfhack_pen_funcs[] = {
{ "parse", dfhack_pen_parse },
{ "make", dfhack_pen_make },
{ "__index", dfhack_pen_index },
{ "__pairs", dfhack_pen_pairs },
{ "__newindex", dfhack_pen_newindex },
{ NULL, NULL }
};
static void OpenPen(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "pen");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
luaL_setfuncs(state, dfhack_pen_funcs, 0);
lua_pop(state, 1);
}
/************************ /************************
* Wrappers for C++ API * * Wrappers for C++ API *
************************/ ************************/
@ -1251,7 +1561,7 @@ static int screen_getWindowSize(lua_State *L)
static int screen_paintTile(lua_State *L) static int screen_paintTile(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
if (lua_gettop(L) >= 4 && !lua_isnil(L, 4)) if (lua_gettop(L) >= 4 && !lua_isnil(L, 4))
@ -1272,44 +1582,14 @@ static int screen_readTile(lua_State *L)
int x = luaL_checkint(L, 1); int x = luaL_checkint(L, 1);
int y = luaL_checkint(L, 2); int y = luaL_checkint(L, 2);
Pen pen = Screen::readTile(x, y); Pen pen = Screen::readTile(x, y);
Lua::Push(L, pen);
if (!pen.valid())
{
lua_pushnil(L);
}
else
{
lua_newtable(L);
lua_pushinteger(L, pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
break;
}
}
}
return 1; return 1;
} }
static int screen_paintString(lua_State *L) static int screen_paintString(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
const char *text = luaL_checkstring(L, 4); const char *text = luaL_checkstring(L, 4);
@ -1320,7 +1600,7 @@ static int screen_paintString(lua_State *L)
static int screen_fillRect(lua_State *L) static int screen_fillRect(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x1 = luaL_checkint(L, 2); int x1 = luaL_checkint(L, 2);
int y1 = luaL_checkint(L, 3); int y1 = luaL_checkint(L, 3);
int x2 = luaL_checkint(L, 4); int x2 = luaL_checkint(L, 4);
@ -1720,6 +2000,7 @@ void OpenDFHackApi(lua_State *state)
{ {
OpenPersistent(state); OpenPersistent(state);
OpenMatinfo(state); OpenMatinfo(state);
OpenPen(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module); LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "gui", dfhack_gui_module);

@ -41,6 +41,9 @@ namespace DFHack {
namespace Units { namespace Units {
struct NoblePosition; struct NoblePosition;
} }
namespace Screen {
struct Pen;
};
} }
namespace DFHack {namespace Lua { namespace DFHack {namespace Lua {
@ -285,6 +288,7 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj);
void Push(lua_State *state, const Units::NoblePosition &pos); void Push(lua_State *state, const Units::NoblePosition &pos);
DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info); DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info);
DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info);
template<class T> inline void Push(lua_State *state, T *ptr) { template<class T> inline void Push(lua_State *state, T *ptr) {
PushDFObject(state, ptr); PushDFObject(state, ptr);
} }
@ -315,6 +319,8 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos);
DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true);
DFHACK_EXPORT bool IsCoreContext(lua_State *state); DFHACK_EXPORT bool IsCoreContext(lua_State *state);
namespace Event { namespace Event {

@ -76,6 +76,8 @@ namespace DFHack
bool valid() const { return tile >= 0; } bool valid() const { return tile >= 0; }
bool empty() const { return ch == 0 && tile == 0; } bool empty() const { return ch == 0 && tile == 0; }
// 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) 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), bold(!!(fg&8)),
tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0)

@ -6,7 +6,9 @@ local dscreen = dfhack.screen
USE_GRAPHICS = dscreen.inGraphicsMode() USE_GRAPHICS = dscreen.inGraphicsMode()
CLEAR_PEN = {ch=32,fg=0,bg=0} local to_pen = dfhack.pen.parse
CLEAR_PEN = to_pen{ch=32,fg=0,bg=0}
function simulateInput(screen,...) function simulateInput(screen,...)
local keys = {} local keys = {}
@ -116,16 +118,6 @@ function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0 return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end end
function to_pen(default, pen, bg, bold)
if pen == nil then
return default or {}
elseif type(pen) ~= 'table' then
return {fg=pen,bg=bg,bold=bold}
else
return pen
end
end
function getKeyDisplay(code) function getKeyDisplay(code)
if type(code) == 'string' then if type(code) == 'string' then
code = df.interface_key[code] code = df.interface_key[code]
@ -215,7 +207,8 @@ Painter = defclass(Painter, ViewRect)
function Painter:init(args) function Painter:init(args)
self.x = self.x1 self.x = self.x1
self.y = self.y1 self.y = self.y1
self.cur_pen = to_pen(nil, args.pen or COLOR_GREY) self.cur_pen = to_pen(args.pen or COLOR_GREY)
self.cur_key_pen = to_pen(args.key_pen or COLOR_LIGHTGREEN)
end end
function Painter.new(rect, pen) function Painter.new(rect, pen)
@ -241,6 +234,7 @@ end
function Painter:viewport(x,y,w,h) function Painter:viewport(x,y,w,h)
local vp = ViewRect.viewport(x,y,w,h) local vp = ViewRect.viewport(x,y,w,h)
vp.cur_pen = self.cur_pen vp.cur_pen = self.cur_pen
vp.cur_key_pen = self.cur_key_pen
return mkinstance(Painter, vp):seek(0,0) return mkinstance(Painter, vp):seek(0,0)
end end
@ -280,10 +274,12 @@ function Painter:pen(pen,...)
end end
function Painter:color(fg,bold,bg) function Painter:color(fg,bold,bg)
self.cur_pen = copyall(self.cur_pen) self.cur_pen = to_pen(self.cur_pen, fg, bg, bold)
self.cur_pen.fg = fg return self
self.cur_pen.bold = bold end
if bg then self.cur_pen.bg = bg end
function Painter:key_pen(pen,...)
self.cur_key_pen = to_pen(self.cur_key_pen, pen, ...)
return self return self
end end
@ -339,10 +335,10 @@ function Painter:string(text,pen,...)
return self:advance(#text, nil) return self:advance(#text, nil)
end end
function Painter:key(code,pen,bg,...) function Painter:key(code,pen,...)
return self:string( return self:string(
getKeyDisplay(code), getKeyDisplay(code),
pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... to_pen(self.cur_key_pen, pen, ...)
) )
end end
@ -557,28 +553,28 @@ end
-- Plain grey-colored frame. -- Plain grey-colored frame.
GREY_FRAME = { GREY_FRAME = {
frame_pen = { ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY }, frame_pen = to_pen{ ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY },
title_pen = { fg = COLOR_BLACK, bg = COLOR_WHITE }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_WHITE },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
} }
-- The usual boundary used by the DF screens. Often has fancy pattern in tilesets. -- The usual boundary used by the DF screens. Often has fancy pattern in tilesets.
BOUNDARY_FRAME = { BOUNDARY_FRAME = {
frame_pen = { ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_DARKGREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_DARKGREY },
} }
GREY_LINE_FRAME = { GREY_LINE_FRAME = {
frame_pen = { ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK },
h_frame_pen = { ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK }, h_frame_pen = to_pen{ ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK },
v_frame_pen = { ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK }, v_frame_pen = to_pen{ ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK },
lt_frame_pen = { ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK }, lt_frame_pen = to_pen{ ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK },
lb_frame_pen = { ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK }, lb_frame_pen = to_pen{ ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK },
rt_frame_pen = { ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK }, rt_frame_pen = to_pen{ ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK },
rb_frame_pen = { ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK }, rb_frame_pen = to_pen{ ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_DARKGREY, bg = COLOR_BLACK }, signature_pen = to_pen{ fg = COLOR_DARKGREY, bg = COLOR_BLACK },
} }
function paint_frame(x1,y1,x2,y2,style,title) function paint_frame(x1,y1,x2,y2,style,title)

@ -160,8 +160,8 @@ function ListBox:preinit(info)
end end
function ListBox:init(info) function ListBox:init(info)
local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false) local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false)
local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
local list_widget = widgets.List local list_widget = widgets.List
if self.with_filter then if self.with_filter then