Как пара цветов фона должна влиять на цвет последующих символов в 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;
}

После компиляции с , куда это код, дает синий текст, если скомпилирован внутри, а если скомпилирован снаружи, вместо этого он дает текст в curses, эквивалентный желтому.


Я изучаю код игры, написанной на C и использующей ncurses. Код инициирует пары цветов, а затем устанавливает пару цветов в качестве фона. Моя проблема в том, что когда я делаю игру из исходного кода, дальнейшее поведение отличается в зависимости от того, (1) я собираю код как обычно в Debian или Fedora или (2) я собираю код с расширением . Как правило, дальнейшая настройка цвета символов работает должным образом, и я получаю фон, перезаписанный цветными символами в местах, где символы появляются на экране. Но если я создам его с помощью , все последующие символы будут отрисованы той же цветовой парой, которой был назначен фон. (Я могу изменить эту пару цветов, и это повлияет на все.)

Если я удалю часть, которая устанавливает пару цветов фона, программы будут работать должным образом в обоих случаях: пара цветов по умолчанию выглядит как белая на черном, а дополнительные символы окрашены в соответствии с кодом.

Я прокомментировал строку ниже, удаление которой помогает.

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

Я пробовал собирать с версиями 6.2.20200212 и 6.2.20210619. Версии в Debian, с которыми я пытался работать, были 6.2+20201114-2 и 6.3-2. Версия для Fedora — 6.2.20210508.

Разница, похоже, не вызвана переменными среды, поскольку я могу воспроизвести результат в том же терминале на Debian, имея локально скомпилированный, репозиторий и версии одного и того же пакета. Кроме того, я могу изменить пару цветов фона, изменив исходный код, чтобы терминал правильно определялся как поддерживающий цвет.

Согласно страницам руководства ( ):

  • Библиотека сначала сравнивает символ, и если он совпадает с частью фона текущего символа, она заменяет его новым фоновым персонажем.
  • Затем библиотека проверяет, использует ли ячейка цвет, т. е. имеет ли ее значение пары цветов ненулевое значение. Если нет, то он просто заменяет атрибуты и пару цветов в ячейке на те, что из нового фонового символа.
  • Если ячейка использует цвет, и он соответствует цвету текущего фона, библиотека удаляет атрибуты, которые могли исходить от текущего фона, и добавляет атрибуты из нового фона. Он заканчивается установкой ячейки для использования цвета нового фона.
  • Если в ячейке используется цвет, который не соответствует цвету текущего фона, библиотека обновляет только нецветовые атрибуты, сначала удаляя те, которые могли исходить от текущего фона, а затем добавляя атрибуты из нового фона.

Я так понимаю, что по какой-то причине в ситуации новые символы считаются находящимися на заднем плане и не окрашены, как ожидалось, а наследуют пару цветов фона, в то время как в обычной ситуации они были бы окрашены, как ожидалось.

Что может повлиять на настройку цвета символов? Это ошибка в библиотеке или вариант реализации? Удаление часть работает, но я не уверен, должен ли я сообщить об этом как об ошибке авторам программного обеспечения или против библиотеку в Guix или просто забудьте о ней.

1 ответ

После звонка bkgd, последующие вызовы для использования цвета фона, если только waddchпозвонить (или wattrset) указал цвет. Это сделано вrender_charфункция 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);
}

Однако в ncurses 6.2 была ошибка, как обсуждалось в списке рассылки в марте 2020 года , о чем сообщалось в guix 2.0. Был простой обходной путь для вызывающих абонентов (который может повлиять на некоторых пользователей). Версия guix не указана в вопросе.

Другие вопросы по тегам