c-背景色对应该如何影响ncurses中后续字符的颜色



我能够用这个小示例代码重现我的问题:

#include <ncurses.h>
int main() {
initscr();
start_color();
init_pair ( 1, COLOR_BLUE, COLOR_BLACK );
init_pair ( 2, COLOR_YELLOW, COLOR_BLACK );
bkgd ( (chtype) COLOR_PAIR(1) );
attrset(COLOR_PAIR(2));
printw("NO WAR!");
refresh();
getch();
endwin();
return 0;
}

一旦用gcc test.c -lncurses编译,其中test.c是代码,如果在guix中编译,./a.out会给出蓝色文本;如果在guix之外编译,则会给出相当于黄色的诅咒文本。


我正在研究用C编写并使用ncurses的游戏代码。该代码启动颜色对,然后将背景设置为颜色对。我的问题是,当我用源代码制作游戏时,进一步的行为会有所不同,这取决于(1(我是像往常一样在Debian或Fedora中构建代码,还是(2(我是用guix构建代码。通常,字符颜色的进一步设置可以按预期工作,我会在屏幕上出现字符的地方用彩色字符覆盖背景。但如果我用guix构建它,所有其他字符都会被绘制在与背景相同的颜色对中。(我可以更改颜色对,它会影响一切。(

如果我删除了设置背景颜色对的部分,程序在这两种情况下都能正常工作,默认的颜色对看起来是黑上白,其他字符根据代码着色。

我在下面的一行上发表了评论,删除它会有所帮助。

/* set up colors */
(void) start_color();
if ( has_colors() && ( COLOR_PAIRS > 7 ) ) {
state.options |= OPTION_HAS_COLOR;
(void) init_pair ( 1, COLOR_GREEN, COLOR_BLACK );
(void) init_pair ( 2, COLOR_RED , COLOR_BLACK );
(void) init_pair ( 3, COLOR_YELLOW, COLOR_BLACK );
(void) init_pair ( 4, COLOR_BLUE, COLOR_BLACK );
(void) init_pair ( 5, COLOR_MAGENTA, COLOR_BLACK );
(void) init_pair ( 6, COLOR_CYAN, COLOR_BLACK );
(void) init_pair ( 7, COLOR_WHITE, COLOR_BLACK );

/* Removing the following line helps */
(void) bkgd ( (chtype) COLOR_PAIR(WHITE) );
state.items[ROBOT].color = WHITE;
state.items[KITTEN].color = randcolor();
for ( i = BOGUS; i < state.num_items; i++ ) {
state.items[i].color = randcolor();
}
} else {
state.options &= ~ OPTION_HAS_COLOR;
}
}
/*@-globstate@*/
static void draw ( const screen_object *o ) {
attr_t new;
/*@-nullpass@*/
assert ( curscr != NULL);
if ( ( state.options & OPTION_HAS_COLOR ) != 0 ) {
new = COLOR_PAIR(o->color);
if ( o->bold ) { new |= A_BOLD; }
if ( o->reverse ) { new |= A_REVERSE; }
(void) attrset ( new );
}
(void) addch ( o->character );
/*@+nullpass@*/
}

完整的C源:https://github.com/robotfindskitten/robotfindskitten/blob/main/src/robotfindskitten.c

我尝试使用guix针对ncurses版本6.2.20200212和6.2.20210619进行构建。我尝试在Debian中制作的版本是6.2+20201114-2和6.3-2。Fedora上的版本是6.2.20210508。

这种差异似乎不是由环境变量引起的,因为我可以在Debian上的同一个终端中复制结果,该终端具有相同包的本地编译、存储库和guix版本。此外,我可以通过修改源代码来更改背景颜色对,这样终端就可以正确地识别为支持颜色。

根据ncurses手册页(man bkgd(:

  • 库首先比较字符,如果它与背景的当前字符部分匹配,则用新的背景字符替换该字符
  • 然后,库检查单元是否使用颜色,即其颜色对值是否为非零。如果没有,它只需将单元格中的属性和颜色对替换为新背景字符中的属性
  • 如果单元格使用的颜色与当前背景中的颜色匹配,则库将删除可能来自当前背景的属性,并添加来自新背景的属性。最后将单元格设置为使用新背景中的颜色
  • 如果单元格使用的颜色与当前背景中的颜色不匹配,则库仅更新非颜色属性,首先删除那些可能来自当前背景的属性,然后添加来自新背景的属性

我的理解是,出于某种原因,在guix的情况下,新字符被认为在背景中,并且没有按预期着色,而是继承了背景色对,而在通常情况下,它们会按预期着色。

什么可能影响字符颜色的设置?它是库中的bug还是实现中的变体?删除bkgd部分是有效的,但我不确定我是应该将其作为错误报告给软件作者,还是针对Guix的ncurses库,或者干脆忘记它。

调用bkgd后,对waddch的后续调用将使用背景色,除非waddch调用(或wattrset(指定了颜色。这是在ncurses:的render_char函数中完成的

static NCURSES_INLINE NCURSES_CH_T
render_char(WINDOW *win, NCURSES_CH_T ch)
/* compute a rendition of the given char correct for the current context */
{
attr_t a = WINDOW_ATTRS(win);
int pair = GetPair(ch);
if (ISBLANK(ch)
&& AttrOf(ch) == A_NORMAL
&& pair == 0) {
/* color/pair in attrs has precedence over bkgrnd */
ch = win->_nc_bkgd;
SetAttr(ch, a | AttrOf(win->_nc_bkgd));
if ((pair = GET_WINDOW_PAIR(win)) == 0)
pair = GetPair(win->_nc_bkgd);
SetPair(ch, pair);
} else {
/* color in attrs has precedence over bkgrnd */
a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a);
/* color in ch has precedence */
if (pair == 0) {
if ((pair = GET_WINDOW_PAIR(win)) == 0)
pair = GetPair(win->_nc_bkgd);
}
AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
SetPair(ch, pair);
}
TR(TRACE_VIRTPUT,
("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)",
_tracech_t2(1, CHREF(win->_nc_bkgd)),
GetPair(win->_nc_bkgd),
_traceattr(WINDOW_ATTRS(win)),
GET_WINDOW_PAIR(win),
_tracech_t2(3, CHREF(ch)),
GetPair(ch)));
return (ch);
}

然而,正如2020年3月用guix 2.0报告的邮件列表中所讨论的那样,ncurses 6.2中存在一个错误。对于呼叫者有一个简单的解决方法(可能会影响一些用户(。问题中没有说明指南的版本。

最新更新