-/* 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.
*/
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));
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
/*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;
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;
}
}
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 */
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 */
* - 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:
* 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()
}
}
+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
* [**] 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;
}
}
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 */
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;
}
}