Curses: implement the windowcolors option

Allow changing the curses windows foreground and background colors,
for example:

OPTIONS=windowcolor:menu #8000F0/20F080 message grey/blue
This commit is contained in:
Pasi Kallinen
2024-03-17 18:25:35 +02:00
parent 9dbab97a1e
commit 4030ef13a0
17 changed files with 214 additions and 32 deletions

View File

@@ -6,6 +6,9 @@
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursinit.h"
#include "curswins.h"
#include "cursmisc.h"
#include "cursdial.h"
#include "func_tab.h"
#include <ctype.h>
@@ -151,8 +154,9 @@ curses_line_input_dialog(
free(tmpstr);
}
bwin = curses_create_window(prompt_width, height,
bwin = curses_create_window(TEXT_WIN, prompt_width, height,
iflags.window_inited ? UP : CENTER);
curses_set_wid_colors(TEXT_WIN, bwin);
wrefresh(bwin);
getbegyx(bwin, winy, winx);
askwin = newwin(height, prompt_width, winy + 1, winx + 1);
@@ -277,7 +281,7 @@ curses_character_input_dialog(
}
if (iflags.wc_popup_dialog /*|| curses_stupid_hack*/) {
askwin = curses_create_window(prompt_width, prompt_height, UP);
askwin = curses_create_window(TEXT_WIN, prompt_width, prompt_height, UP);
activemenu = askwin;
for (count = 0; count < prompt_height; count++) {
@@ -286,6 +290,7 @@ curses_character_input_dialog(
free(linestr);
}
curses_set_wid_colors(TEXT_WIN, askwin);
wrefresh(askwin);
} else {
/* TODO: add SUPPRESS_HISTORY flag, then after getting a response,
@@ -392,7 +397,8 @@ curses_ext_cmd(void)
if (iflags.wc_popup_dialog) { /* Prompt in popup window */
int x0, y0, w, h; /* bounding coords of popup */
extwin2 = curses_create_window(25, 1, UP);
extwin2 = curses_create_window(TEXT_WIN, 25, 1, UP);
curses_set_wid_colors(TEXT_WIN, extwin2);
wrefresh(extwin2);
/* create window inside window to prevent overwriting of border */
getbegyx(extwin2, y0, x0);
@@ -784,13 +790,14 @@ curses_display_nhmenu(
/* Display pre and post-game menus centered */
if ((gm.moves <= 1 && !gi.invent) || gp.program_state.gameover) {
win = curses_create_window(current_menu->width,
win = curses_create_window(wid, current_menu->width,
current_menu->height, CENTER);
} else { /* Display during-game menus on the right out of the way */
win = curses_create_window(current_menu->width,
win = curses_create_window(wid, current_menu->width,
current_menu->height, RIGHT);
}
curses_set_wid_colors(wid, win);
num_chosen = menu_get_selections(win, current_menu, how);
curses_destroy_win(win);
@@ -1337,9 +1344,11 @@ menu_display_page(
curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF);
}
}
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
if (curses_win_clr_inited(MENU_WIN) < 1)
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
box(win, 0, 0);
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
if (curses_win_clr_inited(MENU_WIN) < 1)
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
wrefresh(win);
}

View File

@@ -297,6 +297,36 @@ curses_create_main_windows(void)
}
}
static int pairs_used = 0;
static int colors_used = 0;
/* create a new color */
int
curses_init_rgb(int r, int g, int b)
{
if (!can_change_color())
return 0;
if (colors_used < COLORS - 1) {
colors_used++;
init_color(colors_used, r*4, g*4, b*4);
return colors_used;
}
return 0;
}
/* create a new foreground/background combination */
int
curses_init_pair(int fg, int bg)
{
if (pairs_used < COLOR_PAIRS - 1) {
pairs_used++;
init_pair(pairs_used, fg, bg);
return pairs_used;
}
return 0;
}
/* Initialize curses colors to colors used by NetHack */
void
curses_init_nhcolors(void)
@@ -335,6 +365,8 @@ curses_init_nhcolors(void)
}
}
#endif
colors_used = maxc;
pairs_used = (maxc * 8) + 16 + 1;
}
#if 0 /* curses_choose_character + curses_character_dialog no longer used */

View File

@@ -9,6 +9,8 @@
/* Global declarations */
void curses_create_main_windows(void);
int curses_init_rgb(int r, int g, int b);
int curses_init_pair(int fg, int bg);
void curses_init_nhcolors(void);
void curses_choose_character(void);
int curses_character_dialog(const char **choices, const char *prompt);

View File

@@ -54,7 +54,7 @@ static char *dummy_get_color_string(void);
struct window_procs curses_procs = {
WPID(curses),
(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR | WC_INVERSE
| WC_HILITE_PET
| WC_HILITE_PET | WC_WINDOWCOLORS
#ifdef NCURSES_MOUSE_VERSION /* (this macro name works for PDCURSES too) */
| WC_MOUSE_SUPPORT
#endif
@@ -426,6 +426,10 @@ curses_create_nhwindow(int type)
{
winid wid = curses_get_wid(type);
if (curses_is_menu(wid))
curses_parse_wid_colors(MENU_WIN, iflags.wc_foregrnd_menu, iflags.wc_backgrnd_menu);
else if (curses_is_text(wid))
curses_parse_wid_colors(TEXT_WIN, iflags.wc_foregrnd_text, iflags.wc_backgrnd_text);
if (curses_is_menu(wid) || curses_is_text(wid)) {
curses_start_menu(wid, MENU_BEHAVE_STANDARD);
curses_add_wid(wid);

View File

@@ -7,6 +7,7 @@
#include "hack.h"
#include "wincurs.h"
#include "cursmesg.h"
#include "curswins.h"
#include <ctype.h>
/* defined in sys/<foo>/<foo>tty.c or cursmain.c as last resort;
@@ -106,6 +107,7 @@ curses_message_win_puts(const char *message, boolean recursed)
return; /* user has typed ESC to avoid seeing remaining messages. */
}
curses_set_wid_colors(MESSAGE_WIN, NULL);
curses_get_window_size(MESSAGE_WIN, &height, &width);
border_space = (border ? 1 : 0);
if (mx < border_space)
@@ -322,6 +324,7 @@ curses_block(
}
moreattr = !iflags.wc2_guicolor ? (int) A_REVERSE : NONE;
curses_toggle_color_attr(win, MORECOLOR, moreattr, ON);
curses_set_wid_colors(MESSAGE_WIN, NULL);
if (blink) {
wattron(win, A_BLINK);
mvwprintw(win, my, mx, ">"), mx += 1;
@@ -331,6 +334,7 @@ curses_block(
mvwprintw(win, my, mx, ">>"), mx += 2;
}
curses_toggle_color_attr(win, MORECOLOR, moreattr, OFF);
curses_set_wid_colors(MESSAGE_WIN, NULL);
wrefresh(win);
/* cancel mesg suppression; all messages will have had chance to be read */
@@ -379,6 +383,7 @@ curses_clear_unhighlight_message_window(void)
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
turn_lines = 0;
curses_set_wid_colors(MESSAGE_WIN, NULL);
curses_get_window_size(MESSAGE_WIN, &mh, &mw);
if (mh == 1) {
@@ -413,6 +418,7 @@ curses_last_messages(void)
int border = curses_window_has_border(MESSAGE_WIN) ? 1 : 0;
WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
curses_set_wid_colors(MESSAGE_WIN, NULL);
curses_get_window_size(MESSAGE_WIN, &height, &width);
werase(win);
mx = my = border;
@@ -605,6 +611,7 @@ curses_count_window(const char *count_text)
but not for dolook's autodescribe when it refers to a named monster */
if (!countwin)
countwin = newwin(1, messagew, winy, winx);
curses_set_wid_colors(MESSAGE_WIN, NULL);
werase(countwin);
mvwprintw(countwin, 0, 0, "%s", count_text);
@@ -880,6 +887,7 @@ directional_scroll(winid wid, int nlines)
boolean border = curses_window_has_border(wid);
WINDOW *win = curses_get_nhwin(wid);
curses_set_wid_colors(wid, NULL);
curses_get_window_size(wid, &wh, &ww);
if (wh == 1) {
curses_clear_nhwin(wid);

View File

@@ -9,7 +9,10 @@
#include "curses.h"
#include "hack.h"
#include "wincurs.h"
#include "cursinit.h"
#include "cursmisc.h"
#include "curswins.h"
#include "cursstat.h"
/* Window handling for curses interface */
@@ -23,6 +26,9 @@ typedef struct nhw {
int x; /* start of window on terminal (left) */
int y; /* start of window on terminal (top) */
int orientation; /* Placement of window relative to map */
boolean clr_inited; /* fg/bg/colorpair inited? */
int fg, bg; /* foreground, background color index */
int colorpair; /* color pair of fg, bg */
boolean border; /* Whether window has a visible border */
} nethack_window;
@@ -52,7 +58,7 @@ static void clear_map(void);
/* Create a window with the specified size and orientation */
WINDOW *
curses_create_window(int width, int height, orient orientation)
curses_create_window(int wid, int width, int height, orient orientation)
{
int mapx = 0, mapy = 0, maph = 0, mapw = 0;
int startx = 0;
@@ -142,12 +148,48 @@ curses_create_window(int width, int height, orient orientation)
}
win = newwin(height, width, starty, startx);
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
if (curses_is_text(wid))
wid = TEXT_WIN;
else if (curses_is_menu(wid))
wid = MENU_WIN;
if (nhwins[wid].clr_inited < 1)
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
box(win, 0, 0);
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
if (nhwins[wid].clr_inited < 1)
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
return win;
}
int
curses_win_clr_inited(int wid)
{
if (curses_is_text(wid)) {
wid = TEXT_WIN;
} else if (curses_is_menu(wid)) {
wid = MENU_WIN;
}
return nhwins[wid].clr_inited;
}
void
curses_set_wid_colors(int wid, WINDOW *win)
{
if (wid == TEXT_WIN || curses_is_text(wid)) {
wid = TEXT_WIN;
if (!nhwins[wid].clr_inited)
curses_parse_wid_colors(wid, iflags.wc_foregrnd_text, iflags.wc_backgrnd_text);
} else if (wid == MENU_WIN || curses_is_menu(wid)) {
wid = MENU_WIN;
if (!nhwins[wid].clr_inited)
curses_parse_wid_colors(wid, iflags.wc_foregrnd_menu, iflags.wc_backgrnd_menu);
}
/* FIXME: colors and nhwins[] entry for perm invent window */
if (nhwins[wid].clr_inited > 0) {
wbkgd(win ? win : nhwins[wid].curwin, COLOR_PAIR(nhwins[wid].colorpair));
}
}
/* Erase and delete curses window, and refresh standard windows */
@@ -183,6 +225,7 @@ curses_refresh_nethack_windows(void)
refresh();
} else {
if (status_window != NULL) {
curses_set_wid_colors(STATUS_WIN, NULL);
touchwin(status_window);
wnoutrefresh(status_window);
}
@@ -191,6 +234,7 @@ curses_refresh_nethack_windows(void)
wnoutrefresh(map_window);
}
if (message_window != NULL) {
curses_set_wid_colors(MESSAGE_WIN, NULL);
touchwin(message_window);
wnoutrefresh(message_window);
}
@@ -217,6 +261,67 @@ curses_get_nhwin(winid wid)
return nhwins[wid].curwin;
}
boolean
parse_hexstr(char *colorbuf, int *red, int *green, int *blue)
{
int len = colorbuf ? strlen(colorbuf) : 0;
if (len == 7 && colorbuf[0] == '#') {
char tmpbuf[16];
Sprintf(tmpbuf, "0x%c%c", colorbuf[1], colorbuf[2]);
*red = strtol(tmpbuf, NULL, 0);
Sprintf(tmpbuf, "0x%c%c", colorbuf[3], colorbuf[4]);
*green = strtol(tmpbuf, NULL, 0);
Sprintf(tmpbuf, "0x%c%c", colorbuf[5], colorbuf[6]);
*blue = strtol(tmpbuf, NULL, 0);
return TRUE;
}
return FALSE;
}
void
curses_parse_wid_colors(int wid, char *fg, char *bg)
{
if (curses_is_text(wid)) {
wid = TEXT_WIN;
} else if (curses_is_menu(wid)) {
wid = MENU_WIN;
}
if (nhwins[wid].clr_inited)
return;
int nh_fg = fg ? match_str2clr(fg, TRUE) : CLR_MAX;
int nh_bg = bg ? match_str2clr(bg, TRUE) : CLR_MAX;
int r, g, b;
if (nh_fg == CLR_MAX) {
if (fg && parse_hexstr(fg, &r, &g, &b)) {
nh_fg = curses_init_rgb(r, g, b);
} else {
nh_fg = -1;
}
}
if (nh_bg == CLR_MAX) {
if (bg && parse_hexstr(bg, &r, &g, &b)) {
nh_bg = curses_init_rgb(r, g, b);
} else {
nh_bg = -1;
}
}
nhwins[wid].fg = nh_fg;
nhwins[wid].bg = nh_bg;
if (nh_fg == -1 || nh_bg == -1) {
nhwins[wid].clr_inited = -1;
} else {
nhwins[wid].colorpair = curses_init_pair(nh_fg, nh_bg);
nhwins[wid].clr_inited = 1;
}
}
/* Add curses window pointer and window info to list for given NetHack winid */
@@ -241,6 +346,9 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x,
nhwins[wid].x = x;
nhwins[wid].y = y;
nhwins[wid].orientation = orientation;
nhwins[wid].fg = nhwins[wid].bg = 0;
nhwins[wid].colorpair = -1;
nhwins[wid].clr_inited = 0;
if (border) {
real_width += 2; /* leave room for bounding box */
@@ -248,13 +356,18 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x,
}
win = newwin(real_height, real_width, y, x);
nhwins[wid].curwin = win;
switch (wid) {
case MESSAGE_WIN:
messagewin = win;
curses_parse_wid_colors(wid, iflags.wc_foregrnd_message, iflags.wc_backgrnd_message);
curses_set_wid_colors(wid, NULL);
break;
case STATUS_WIN:
statuswin = win;
curses_parse_wid_colors(wid, iflags.wc_foregrnd_status, iflags.wc_backgrnd_status);
curses_set_wid_colors(wid, NULL);
break;
case MAP_WIN:
mapwin = win;
@@ -266,7 +379,6 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x,
box(win, 0, 0);
}
nhwins[wid].curwin = win;
}
@@ -301,6 +413,7 @@ curses_add_wid(winid wid)
void
curses_refresh_nhwin(winid wid)
{
curses_set_wid_colors(wid, NULL);
wnoutrefresh(curses_get_nhwin(wid));
doupdate();
}
@@ -523,6 +636,7 @@ curses_puts(winid wid, int attr, const char *text)
}
#endif
curses_set_wid_colors(wid, NULL);
if (curses_is_menu(wid) || curses_is_text(wid)) {
if (!curses_menu_exists(wid)) {
impossible(
@@ -553,6 +667,7 @@ curses_clear_nhwin(winid wid)
clear_map();
}
curses_set_wid_colors(wid, NULL);
werase(win);
if (border) {
@@ -568,6 +683,7 @@ curses_alert_win_border(winid wid, boolean onoff)
if (!win || !curses_window_has_border(wid))
return;
curses_set_wid_colors(wid, NULL);
if (onoff)
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, ON);
box(win, 0, 0);

View File

@@ -9,11 +9,15 @@
/* Global declarations */
WINDOW *curses_create_window(int width, int height, orient orientation);
WINDOW *curses_create_window(int wid, int width, int height, orient orientation);
int curses_win_clr_inited(int wid);
void curses_set_wid_colors(int wid, WINDOW *win);
void curses_destroy_win(WINDOW * win);
void curses_refresh_nethack_windows(void);
WINDOW *curses_get_nhwin(winid wid);
void curses_parse_wid_colors(int wid, char *fg, char *bg);
boolean parse_hexstr(char *colorbuf, int *red, int *green, int *blue);
void curses_add_nhwin(winid wid, int height, int width, int y, int x,
orient orientation, boolean border);
void curses_add_wid(winid wid);