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

发布于 2025-01-16 21:14:12 字数 3449 浏览 5 评论 0原文

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

#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 之外编译,它会给出与黄色等价的curses 文本。


我正在研究用 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 情况下,新字符被认为是在背景中,并且没有按预期着色,而是继承背景颜色对,而在通常情况下,它们将按预期着色。

哪些因素会影响角色颜色的设置?这是库中的错误还是实现中的变化?删除 bkgd 部分是可行的,但我不确定是否应该将其作为错误报告给软件作者或针对 Guix 的 ncurses 库,或者干脆忘记它。

I was able to reproduce my issue with this small example code:

#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;
}

Once compiled with gcc test.c -lncurses, where test.c is that code, ./a.out gives blue text if compiled in guix and, if compiled outside guix, it gives text in the curses equivalent of yellow instead.


I am looking into the code of a game written in C and using ncurses. The code initiates color pairs, then sets the background to a color pair. My issue is that when I make the game from source code, the further behavior is different depending on whether (1) I build the code as usual in Debian or Fedora or whether (2) I build the code with guix. Typically, further setting of color of characters works as expected and I get the background overwritten with colored characters in places where the characters appear on screen. But if I build it with guix, all further characters are drawn in the same color pair which the background was assigned to. (I can change that color pair and it affects everything.)

If I remove the part which sets the background color pair, the programs works as expected in both cases with the default color pair being seemingly white-on-black and further characters colored according to the code.

I commented over the line below, removing which helps.

    /* 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@*/
}

The complete C source: https://github.com/robotfindskitten/robotfindskitten/blob/main/src/robotfindskitten.c

I tried building with guix against ncurses versions 6.2.20200212 and 6.2.20210619. The versions in Debian which I tried making against were 6.2+20201114-2 and 6.3-2. The version on Fedora is 6.2.20210508.

The difference does not seem to be caused by environment variables, since I can reproduce the result within the same terminal on Debian having locally compiled, repository, and guix versions of the same package. Also, I can change the background color pair by modifying the source code, so the terminal is properly identified as supporting color.

According to ncurses manual pages (man bkgd):

  • The library first compares the character, and if it matches the current character part of the background, it replaces that with the new background character.
  • The library then checks if the cell uses color, i.e., its color pair value is nonzero. If not, it simply replaces the attributes and color pair in the cell with those from the new background character.
  • If the cell uses color, and that matches the color in the current background, the library removes attributes which may have come from the current background and adds attributes from the new background. It finishes by setting the cell to use the color from the new background.
  • If the cell uses color, and that does not match the color in the current background, the library updates only the non-color attributes, first removing those which may have come from the current background, and then adding attributes from the new background.

My understanding is that, for some reason, in guix situation, the new characters are considered to be in the background and are not colored as expected but inherit the background color pair, while, in usual situation, they would be colored as expected.

What may affect the setting of characters' color? Is it a bug in a library or a variation in implementation? Removing bkgd part works, but I am not sure whether I should report it as a bug to the software authors or against ncurses library at Guix or just forget about it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

缪败 2025-01-23 21:14:12

调用 bkgd 后,后续对 waddch 的调用将使用背景颜色,除非 waddch 调用(或 wattrset)已指定一种颜色。这是在 render_charncurses 的功能:

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);
}

但是,ncurses 6.2 中存在一个错误,正如 2020 年 3 月 报道了 guix 2.0。对于调用者有一个简单的解决方法(这可能会影响某些用户)。问题中没有说明guix的版本。

After calling bkgd, subsequent calls to waddch use the background color unless the waddch call (or wattrset) has specified a color. That's done in the render_char function of ncurses:

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);
}

However, there was a bug in ncurses 6.2 as discussed on the mailing list in March 2020 reported with guix 2.0. There was a simple workaround for callers (which may affect some users). The version of guix is not stated in the question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文