From: PatR Date: Tue, 26 Jan 2021 21:39:50 +0000 (-0800) Subject: pull request #443 - X11 color status highlighting X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=acbf423734a3b09a47c2c7329cf18404234016c8;p=nethack pull request #443 - X11 color status highlighting for 'fancy status'. This is from an emailed diff rather than directly from git, and the git code has a bunch of commits, so this may or may not match the latest. It needs formatting cleanup and triggers a couple of warnings on OSX. Fix to follow. Status highlight colors use the same names as menu coloring but this uses different X11 colors for the two sets. That will have to be changed so that yellow either means yellow all the time or goldenrod all the time instead of sometimes yellow and sometimes goldenrod. Adopts #443 --- diff --git a/include/winX.h b/include/winX.h index 4d7689c04..03244d04d 100644 --- a/include/winX.h +++ b/include/winX.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 winX.h $NHDT-Date: 1596498574 2020/08/03 23:49:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.39 $ */ +/* NetHack 3.7 winX.h $NHDT-Date: 1611697182 2021/01/26 21:39:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.42 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -419,6 +419,7 @@ E Widget FDECL(create_value, (Widget, const char *)); E void FDECL(set_name, (Widget, const char *)); E void FDECL(set_name_width, (Widget, int)); E int FDECL(get_name_width, (Widget)); +E Widget FDECL(get_value_widget, (Widget)); E void FDECL(set_value, (Widget, const char *)); E void FDECL(set_value_width, (Widget, int)); E int FDECL(get_value_width, (Widget)); diff --git a/win/X11/winstat.c b/win/X11/winstat.c index cbc0ce078..bb82c91f4 100644 --- a/win/X11/winstat.c +++ b/win/X11/winstat.c @@ -1,11 +1,11 @@ -/* NetHack 3.7 winstat.c $NHDT-Date: 1596498375 2020/08/03 23:46:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.7 winstat.c $NHDT-Date: 1611697183 2021/01/26 21:39:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ /* * Status window routines. This file supports both the "traditional" * tty status display and a "fancy" status display. A tty status is - * made if a popup window is requested, otherewise a fancy status is + * made if a popup window is requested, otherwise a fancy status is * made. This code assumes that only one fancy status will ever be made. * Currently, only one status window (of any type) is _ever_ made. */ @@ -107,6 +107,7 @@ static void FDECL(tt_reset_color, (int, int, unsigned long *)); static void NDECL(tt_status_fixup); static Widget FDECL(create_tty_status_field, (int, int, Widget, Widget)); static Widget FDECL(create_tty_status, (Widget, Widget)); +static void FDECL(update_fancy_status_field_with_hilites, (int, int, int)); static void FDECL(update_fancy_status_field, (int)); static void FDECL(update_fancy_status, (BOOLEAN_P)); static Widget FDECL(create_fancy_status, (Widget, Widget)); @@ -178,6 +179,25 @@ static Widget X11_cond_labels[32]; /* Ugh */ static XFontStruct *X11_status_font; static Pixel X11_status_fg, X11_status_bg; +static const char* fancy_status_hilite_colors[] = { + "grey15", + "red3", + "dark green", + "saddle brown", + "blue", + "magenta3", + "dark cyan", + "web gray", + "", /* NO_COLOR */ + "orange", + "green3", + "goldenrod", + "royal blue", + "magenta", + "cyan", + "white" +}; + struct xwindow *xw_status_win; static int @@ -812,9 +832,9 @@ unsigned long *colormasks; /* bitmask of highlights for conditions */ /*ARGSUSED*/ static void X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks) -int fld, chg UNUSED, percent UNUSED, color UNUSED; +int fld, chg UNUSED, percent UNUSED, color; genericptr_t ptr; -unsigned long *colormasks UNUSED; +unsigned long *colormasks; { static const struct bl_to_ff { int bl, ff; @@ -883,13 +903,16 @@ unsigned long *colormasks UNUSED; if (changed_bits) { for (i = 0; i < SIZE(mask_to_fancyfield); i++) if ((changed_bits & mask_to_fancyfield[i].mask) != 0L) - update_fancy_status_field(mask_to_fancyfield[i].ff); + update_fancy_status_field_with_hilites(mask_to_fancyfield[i].ff, + condcolor(mask_to_fancyfield[i].mask, colormasks), + condattr(mask_to_fancyfield[i].mask, colormasks)); old_condition_bits = X11_condition_bits; /* remember 'On' bits */ } } else { for (i = 0; i < SIZE(bl_to_fancyfield); i++) if (bl_to_fancyfield[i].bl == fld) { - update_fancy_status_field(bl_to_fancyfield[i].ff); + update_fancy_status_field_with_hilites(bl_to_fancyfield[i].ff, + color & 0xf, color >> 8 & 0xff); break; } } @@ -1228,8 +1251,6 @@ const char *str; extern const char *hu_stat[]; /* from eat.c */ extern const char *enc_stat[]; /* from botl.c */ -static int hilight_time = 1; /* number of turns to hilight a changed value */ - struct X_status_value { /* we have to cast away 'const' when assigning new names */ const char *name; /* text name */ @@ -1239,6 +1260,8 @@ struct X_status_value { int turn_count; /* last time the value changed */ boolean set; /* if highlighted */ boolean after_init; /* don't highlight on first change (init) */ + boolean inverted_hilite; /* if highlighted due to hilite_status inverse rule */ + Pixel default_fg; /* what FG color it initialized with */ }; /* valid type values */ @@ -1280,53 +1303,53 @@ static Widget FDECL(init_info_form, (Widget, Widget, Widget)); * - These must be in the same order as the F_foo numbers. */ static struct X_status_value shown_stats[NUM_STATS] = { - { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, FALSE }, /* 0 */ - - { "Strength", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, /* 1*/ - { "Dexterity", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Constitution", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Intelligence", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Wisdom", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, /* 5*/ - { "Charisma", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - - { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE }, /*NAME*/ - { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE }, /*DLEVEL*/ - { "Gold", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Hit Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, /*10*/ - { "Max HP", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Max Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Armor Class", SV_VALUE, (Widget) 0, 256L, 0, FALSE, FALSE }, - { "Xp Level", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, /*15*/ - /*{ "Hit Dice", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, ==15*/ - { "Exp Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Alignment", SV_VALUE, (Widget) 0, -2L, 0, FALSE, FALSE }, - { "Time", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, - { "Score", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE }, /*19*/ - - { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, TRUE }, /*20 */ - { "", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*ENCMBR*/ - { "Trapped", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Tethered", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Levitating", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Flying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*25*/ - { "Riding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - - { "Grabbed!", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*27*/ - { "Petrifying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*STONE*/ - { "Slimed", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Strangled", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*30*/ - { "Food Pois", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Term Ill", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Sinking", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*LAVA*/ - - { "Held", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*34*/ - { "Holding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*35*/ - { "Blind", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Deaf", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Stunned", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Confused", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, - { "Hallucinat", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE }, /*40*/ + { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /* 0 */ + + { "Strength", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /* 1*/ + { "Dexterity", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Constitution", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Intelligence", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Wisdom", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /* 5*/ + { "Charisma", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + + { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /*NAME*/ + { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /*DLEVEL*/ + { "Gold", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Hit Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /*10*/ + { "Max HP", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Max Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Armor Class", SV_VALUE, (Widget) 0, 256L, 0, FALSE, FALSE, FALSE, 0 }, + { "Xp Level", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /*15*/ + /*{ "Hit Dice", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, ==15*/ + { "Exp Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Alignment", SV_VALUE, (Widget) 0, -2L, 0, FALSE, FALSE, FALSE, 0 }, + { "Time", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, + { "Score", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0 }, /*19*/ + + { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, TRUE, FALSE, 0 }, /*20 */ + { "", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*ENCMBR*/ + { "Trapped", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Tethered", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Levitating", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Flying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*25*/ + { "Riding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + + { "Grabbed!", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*27*/ + { "Petrifying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*STONE*/ + { "Slimed", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Strangled", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*30*/ + { "Food Pois", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Term Ill", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Sinking", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*LAVA*/ + + { "Held", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*34*/ + { "Holding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*35*/ + { "Blind", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Deaf", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Stunned", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Confused", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, + { "Hallucinat", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0 }, /*40*/ }; /* * The following are supported by the core but not yet handled here: @@ -1625,23 +1648,26 @@ long new_value; * or changing to not-hungry or not-encumbered, there's nothing to * highlight because the field becomes blank. */ - if (attr_rec != &shown_stats[F_TIME]) { - if (attr_rec->after_init) { - /* toggle if not highlighted and just set to nonblank or if - already highlighted and just set to blank */ - if (!attr_rec->set ^ !*buf) { - if (attr_rec->type == SV_LABEL || attr_rec->type == SV_NAME) - hilight_label(attr_rec->w); - else - hilight_value(attr_rec->w); - - attr_rec->set = !attr_rec->set; - } - attr_rec->turn_count = 0; - } else { - attr_rec->after_init = TRUE; - } - } + if (attr_rec->after_init) { + /* toggle if not highlighted and just set to nonblank or if + already highlighted and just set to blank */ + if (attr_rec != &shown_stats[F_TIME] && !attr_rec->set ^ !*buf) { + /* But don't hilite if inverted from status_hilite since + it will already be hilited by apply_hilite_attributes(). */ + if (!attr_rec->inverted_hilite) { + if (attr_rec->type == SV_VALUE) + hilight_value(attr_rec->w); + else + hilight_label(attr_rec->w); + } + attr_rec->set = !attr_rec->set; + } + attr_rec->turn_count = 0; + } else { + XtSetArg(args[0], XtNforeground, &attr_rec->default_fg); + XtGetValues(attr_rec->w, args, ONE); + attr_rec->after_init = TRUE; + } } /* overloaded condition is being cleared without going through update_val() @@ -1661,6 +1687,74 @@ struct X_status_value *sv; } } +static void +update_color(sv, color) +struct X_status_value *sv; +int color; +{ + Pixel pixel; + Arg args[1]; + XrmValue source; + XrmValue dest; + + Widget w = (sv->type == SV_LABEL || sv->type == SV_NAME ? sv->w : get_value_widget(sv->w)); + if (color == NO_COLOR) { + if (sv->after_init) + pixel = sv->default_fg; + } + else { + source.size = strlen(fancy_status_hilite_colors[color]) + 1; + source.addr = (char *)fancy_status_hilite_colors[color]; + dest.size = sizeof(Pixel); + dest.addr = (XPointer)&pixel; + if (!XtConvertAndStore(w, XtRString, &source, XtRPixel, &dest)) + pixel = 0; + } + if (pixel != 0) { + char *arg_name = (sv->set || sv->inverted_hilite ? XtNbackground : XtNforeground); + XtSetArg(args[0], arg_name, pixel); + XtSetValues(w, args, ONE); + } +} + +boolean +name_widget_has_label(sv) +struct X_status_value *sv; +{ + Arg args[1]; + const char *label; + + XtSetArg(args[0], XtNlabel, &label); + XtGetValues(sv->w, args, ONE); + return strlen(label) > 0; +} + +static void +apply_hilite_attributes(sv, attributes) +struct X_status_value *sv; +int attributes; +{ + boolean attr_inversion + = HL_INVERSE & attributes + && (sv->type != SV_NAME || name_widget_has_label(sv)) + ? TRUE + : FALSE; + + if (sv->inverted_hilite != attr_inversion) { + sv->inverted_hilite = attr_inversion; + if (!sv->set) { + if (sv->type == SV_VALUE) + hilight_value(sv->w); + else + hilight_label(sv->w); + } + } + /* Could possibly add more attributes here: HL_ATTCLR_DIM, + HL_ATTCLR_BLINK, HL_ATTCLR_ULINE, and HL_ATTCLR_BOLD. If so, + extract the above into its own function apply_hilite_inverse() + and each other attribute into its own to keep the code clean. */ +} + /* * Update the displayed status. The current code in botl.c updates * two lines of information. Both lines are always updated one after @@ -1679,8 +1773,8 @@ struct X_status_value *sv; * [**] HD is shown instead of level and exp if Upolyd. */ static void -update_fancy_status_field(i) -int i; +update_fancy_status_field_with_hilites(i, color, attributes) +int i, color, attributes; { struct X_status_value *sv = &shown_stats[i]; unsigned long condmask = 0L; @@ -1860,6 +1954,17 @@ int i; } } update_val(sv, val); + if (color >= 0 ) + update_color(sv, color); + if (attributes >=0 ) + apply_hilite_attributes(sv, attributes); +} + +static void +update_fancy_status_field(i) +int i; +{ + update_fancy_status_field_with_hilites(i, -1, -1); } /* fully update status after bl_flush or window resize */ @@ -1906,12 +2011,15 @@ check_turn_events() if (!sv->set) continue; - if (sv->turn_count++ >= hilight_time) { - /* unhighlights by toggling a highlighted item back off again */ - if (sv->type == SV_LABEL || sv->type == SV_NAME) - hilight_label(sv->w); - else - hilight_value(sv->w); + if (sv->turn_count++ >= iflags.hilite_delta) { + /* unhighlights by toggling a highlighted item back off again, + unless forced inverted by a status_hilite rule */ + if (!sv->inverted_hilite) { + if (sv->type == SV_VALUE) + hilight_value(sv->w); + else + hilight_label(sv->w); + } sv->set = FALSE; } } diff --git a/win/X11/winval.c b/win/X11/winval.c index dd041012b..6bb620539 100644 --- a/win/X11/winval.c +++ b/win/X11/winval.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 winval.c $NHDT-Date: 1596498377 2020/08/03 23:46:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.10 $ */ +/* NetHack 3.7 winval.c $NHDT-Date: 1611697183 2021/01/26 21:39:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.11 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -114,6 +114,13 @@ Widget w; return (int) width; } +Widget +get_value_widget(w) +Widget w; +{ + return XtNameToWidget(w, WVALUE); +} + void set_value(w, new_value) Widget w; @@ -122,7 +129,7 @@ const char *new_value; Arg args[1]; Widget val; - val = XtNameToWidget(w, WVALUE); + val = get_value_widget(w); XtSetArg(args[0], XtNlabel, new_value); XtSetValues(val, args, ONE); } @@ -135,7 +142,7 @@ int new_width; Arg args[1]; Widget val; - val = XtNameToWidget(w, WVALUE); + val = get_value_widget(w); XtSetArg(args[0], XtNwidth, new_width); XtSetValues(val, args, ONE); } @@ -148,7 +155,7 @@ Widget w; Widget val; Dimension width; - val = XtNameToWidget(w, WVALUE); + val = get_value_widget(w); XtSetArg(args[0], XtNwidth, &width); XtGetValues(val, args, ONE); return (int) width; @@ -160,7 +167,7 @@ void hilight_value(w) Widget w; { - swap_fg_bg(XtNameToWidget(w, WVALUE)); + swap_fg_bg(get_value_widget(w)); } /* Swap the foreground and background colors of the given widget */