From 564affea88cd7653b6e336501e123d863b6d07fe Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 30 Aug 2026 15:52:15 -0400 Subject: [PATCH 1/5] Remove limit in size of widget_table It is difficult, but possible, to exceed 1024 widgets. One way is to set a very large Unicode symbol set, and then use #wizcustom. As there is no limit to how many objects can occupy a square, this limit is a possible hazard even to a normal game. --- win/X11/winlabel.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/win/X11/winlabel.c b/win/X11/winlabel.c index 2f9e5e68c..f8e99510c 100644 --- a/win/X11/winlabel.c +++ b/win/X11/winlabel.c @@ -610,9 +610,9 @@ typedef struct WidgetBucket { WidgetData *data; } WidgetBucket; -#define MAX_WIDGETS 1024 -static WidgetBucket widget_table[MAX_WIDGETS]; -static unsigned num_widgets; +static WidgetBucket *widget_table = NULL; +static unsigned num_widgets = 0; +static unsigned max_widgets = 0; /* bsearch compare function to search widget-table */ static int @@ -653,9 +653,12 @@ get_widget_data(Widget w) static WidgetData * add_widget(Widget w) { - /* Panic rather than overflow the array */ - if (num_widgets >= MAX_WIDGETS) { - panic("Widget table is full\n"); + /* If the array is full (or unallocated), add some new space to it */ + if (num_widgets >= max_widgets) { + max_widgets = num_widgets + 1024; + widget_table = (WidgetBucket *) re_alloc( + (long *) widget_table, + max_widgets * sizeof(widget_table[0])); } /* Insert the widget into the table, maintaining its order */ @@ -688,7 +691,7 @@ delete_widget(Widget w) /* Remove the widget from the table */ for (unsigned i = (unsigned)(bucket - widget_table); - i + 1 < MAX_WIDGETS; + i + 1 < num_widgets; ++i) { widget_table[i] = widget_table[i+1]; } From c88403f4ecd3699dbc080b4f2474afd7818ffdb7 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 30 Aug 2026 16:44:26 -0400 Subject: [PATCH 2/5] Correct display of tabified PICK_NONE menu --- win/X11/winmenu.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c index e8d0eead2..4d0c71c2f 100644 --- a/win/X11/winmenu.c +++ b/win/X11/winmenu.c @@ -1320,12 +1320,10 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) /* Does any line have a selector? */ boolean any_canpick = FALSE; - if (how != PICK_NONE) { - for (curr = curr_menu->base; curr; curr = curr->next) { - if (curr->identifier.a_void != NULL) { - any_canpick = TRUE; - break; - } + for (curr = curr_menu->base; curr; curr = curr->next) { + if (curr->identifier.a_void != NULL) { + any_canpick = TRUE; + break; } } @@ -1342,7 +1340,7 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) String str = (String) curr->str; int attr = ATR_NONE; int color = NO_COLOR; - boolean canpick = (how != PICK_NONE && curr->identifier.a_void); + boolean canpick = curr->identifier.a_void != NULL; /* Add tabs if needed to align non-selector lines with selector lines */ if (any_canpick && !canpick) { From 98815bc7390ae4b494eb4d08f9850ce069440e0a Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 30 Aug 2026 16:44:44 -0400 Subject: [PATCH 3/5] Tabify the #wizcustom menu --- src/wizcmds.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/wizcmds.c b/src/wizcmds.c index fd0568120..41b49da8d 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -1952,9 +1952,11 @@ wiz_custom(void) win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); - add_menu_heading(win, - " glyph glyph identifier " - " sym clr customcolor unicode utf8"); + const char *heading = iflags.menu_tab_sep + ? "glyph\tglyph identifier\tsym\tclr\tcustomcolor\tunicode\tutf8" + : " glyph glyph identifier " + " sym clr customcolor unicode utf8"; + add_menu_heading(win, heading); Sprintf(bufa, "%s: colorcount=%ld %s", wizcustom, (long) iflags.colorcount, gs.symset[PRIMARYSET].name ? gs.symset[PRIMARYSET].name @@ -2001,26 +2003,35 @@ wizcustom_callback(winid win, int glyphnum, char *id) cgm->u || #endif cgm->customcolor != 0) { - Sprintf(bufa, "[%04d] %-44s", glyphnum, id); - Sprintf(bufb, "'\\%03d' %02d", - gs.showsyms[cgm->sym.symidx], cgm->sym.color); - Sprintf(bufc, "%011lx", (unsigned long) cgm->customcolor); + if (iflags.menu_tab_sep) { + Sprintf(bufa, "[%04d]\t%s\t", glyphnum, id); + Sprintf(bufb, "'\\%03d'\t%02d\t", + gs.showsyms[cgm->sym.symidx], cgm->sym.color); + Sprintf(bufc, "%011lx\t", (unsigned long) cgm->customcolor); + } else { + Sprintf(bufa, "[%04d] %-45s", glyphnum, id); + Sprintf(bufb, "'\\%03d' %02d ", + gs.showsyms[cgm->sym.symidx], cgm->sym.color); + Sprintf(bufc, "%011lx ", (unsigned long) cgm->customcolor); + } bufu[0] = '\0'; #ifdef ENHANCED_SYMBOLS if (cgm->u && cgm->u->utf8str) { uint8 *cp; + char sep = iflags.menu_tab_sep ? '\t' : ' '; Sprintf(bufu, "U+%04lx", (unsigned long) cgm->u->utf32ch); cp = cgm->u->utf8str; while (*cp) { char bufd[BUFSZ]; - Sprintf(bufd, " <%d>", (int) *cp); + Sprintf(bufd, "%c<%d>", sep, (int) *cp); Strcat(bufu, bufd); cp++; + sep = ' '; } } #endif any.a_int = glyphnum + 1; /* avoid 0 */ - Snprintf(buf, sizeof buf, "%s %s %s %s", bufa, bufb, bufc, bufu); + Snprintf(buf, sizeof buf, "%s%s%s%s", bufa, bufb, bufc, bufu); add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } From 393130a6293584807cb5e494d833d089dcfcc121 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 30 Aug 2026 17:51:10 -0400 Subject: [PATCH 4/5] Handle tabs in the message window --- win/X11/winmesg.c | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/win/X11/winmesg.c b/win/X11/winmesg.c index ed172c4f8..1678e5da7 100644 --- a/win/X11/winmesg.c +++ b/win/X11/winmesg.c @@ -520,16 +520,42 @@ redraw_message_window(struct xwindow *wp) for (y_base = row = 0, curr = mesg_info->head; row < mesg_info->num_lines; row++, y_base += mesg_info->char_height, curr = curr->next) { -#ifdef USE_XFT - if (curr->line != NULL) { - XftDrawString8(draw, &fgcolor, font, - mesg_info->char_lbearing, mesg_info->char_ascent + y_base, - (const FcChar8 *) curr->line, curr->str_length); + if (curr->line == NULL) { + continue; } + /* Deal with any tabs in the output. These will just be single strings, + * not needing to align columns, so just convert to spaces */ + const char *str = curr->line; + int str_length = curr->str_length; + char buf[BUFSZ]; + if (memchr(str, '\t', str_length) != NULL) { + int i2 = 0; + for (int i1 = 0; i1 < str_length; ++i1) { + if (str[i1] == '\t') { + if (i2 + 4 > BUFSZ) { + break; + } + memcpy(buf + i2, " ", 4); + i2 += 4; + } else { + if (i2 >= BUFSZ) { + break; + } + buf[i2++] = str[i1]; + } + } + /* buf does not need to be null terminated */ + str = buf; + str_length = i2; + } +#ifdef USE_XFT + XftDrawString8(draw, &fgcolor, font, + mesg_info->char_lbearing, mesg_info->char_ascent + y_base, + (const FcChar8 *) str, str_length); #else /* !USE_XFT */ XDrawString(XtDisplay(wp->w), XtWindow(wp->w), mesg_info->gc, mesg_info->char_lbearing, mesg_info->char_ascent + y_base, - curr->line, curr->str_length); + str, str_length); #endif /* ?USE_XFT */ /* * This draws a line at the _top_ of the line of text pointed to by From 2f42bd7e2752730b9580da7dc441943b8561742a Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 30 Aug 2026 19:03:45 -0400 Subject: [PATCH 5/5] Display tabs in the text window Actually a menu, but menu windows convert to text if only displaying text. --- win/X11/winlabel.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ win/X11/wintext.c | 4 +-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/win/X11/winlabel.c b/win/X11/winlabel.c index f8e99510c..5842ef49f 100644 --- a/win/X11/winlabel.c +++ b/win/X11/winlabel.c @@ -54,6 +54,7 @@ enum { font_bold = 1, font_italic = 2 }; static boolean check_label(Widget); static void delete_callback(Widget, XtPointer, XtPointer); static void update_label(Widget, WidgetData *); +static void set_tab_stops(Widget, WidgetData *, const char *); static void allocate_font(Widget, WidgetData *, unsigned); static void free_fonts(Widget, WidgetData *); @@ -242,6 +243,11 @@ update_label(Widget w, WidgetData *data) attrs |= HL_DIM; } + /* Set the tab stops if needed */ + if (data->columns == NULL && strchr(label, '\t') != NULL) { + set_tab_stops(w, data, label); + } + /* Dimensions of pixmap */ Dimension width; Dimension height; @@ -496,6 +502,66 @@ update_label(Widget w, WidgetData *data) data->pixmap = new_pixmap; } +/* Set tab stops for text window */ +static void +set_tab_stops(Widget w, WidgetData *data, const char *label) +{ + /* Count columns */ + unsigned col = 0; + unsigned num_cols = 0; + size_t i = 0; + while (label[i] != '\0') { + size_t len = strcspn(label + i, "\t\n"); + ++col; + i += len; + if (label[i] != '\t') { + /* The last column on its line */ + num_cols = max(num_cols, col); + col = 0; + } + if (label[i] != '\0') { + ++i; + } + } + + int *columns = (int *) alloc(num_cols * sizeof(columns[0])); + memset(columns, 0, num_cols * sizeof(columns[0])); + + /* Determine column sizes */ + col = 0; + i = 0; + Display *display = XtDisplay(w); + while (label[i] != '\0') { + size_t len = strcspn(label + i, "\t\n"); + if (label[i+len] == '\t') { + /* A column from label+i to label+i+len */ + int width = X11_column_width(display, data->font[0], label + i, len); + columns[col] = max(columns[col], width); + ++col; + } else { + /* Last column of the line */ + /* This does not participate in sizing the column */ + col = 0; + } + i += len; + if (label[i] != '\0') { + ++i; + } + } + + /* Convert to column positions */ + int pos = 0; + for (unsigned j = 0; j < num_cols; ++j) { + int rpos = pos + columns[j]; + columns[j] = pos; + pos = rpos; + } + + free(data->columns); + data->columns = columns; + data->num_cols = num_cols; +} + /* Allocate a bold or italic font */ static void allocate_font(Widget w, WidgetData *data, unsigned font_idx) diff --git a/win/X11/wintext.c b/win/X11/wintext.c index ee6247029..a80b6540d 100644 --- a/win/X11/wintext.c +++ b/win/X11/wintext.c @@ -170,7 +170,7 @@ display_text_window(struct xwindow *wp, boolean blocking) XtSetArg(args[num_args], XtNlabel, text_info->text.text); num_args++; XtSetValues(wp->w, args, num_args); - X11_update_label_if_Xft(wp->w); + X11_update_label(wp->w); #ifdef TRANSIENT_TEXT XtRealizeWidget(wp->popup); @@ -259,7 +259,7 @@ create_text_window(struct xwindow *wp) form, /* parent widget */ args, /* set some values */ num_args); /* number of values to set */ - X11_wrap_widget_if_Xft(wp->w, NHW_TEXT); + X11_wrap_widget(wp->w, NHW_TEXT); } void