Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F165090707
D58118.id182190.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
55 KB
Referenced Files
None
Subscribers
None
D58118.id182190.diff
View Options
diff --git a/contrib/bsddialog/CHANGELOG b/contrib/bsddialog/CHANGELOG
--- a/contrib/bsddialog/CHANGELOG
+++ b/contrib/bsddialog/CHANGELOG
@@ -1,3 +1,54 @@
+2026-07-08 Version 1.2
+
+ Library:
+ * add: alternate-screen drawing in bsddialog_init();
+ bsddialog_keep_tite() for callers (e.g. bsdpv) that manage the TITE
+ buffer themselves
+ * add: backdrop and flush helpers: bsddialog_fill_backdrop(),
+ bsddialog_paint_screen(), dialog_backdrop_sync(), dialog_flush(),
+ dialog_flush_overlay(), dialog_flush_widget(), dialog_flush_stdscr(),
+ dialog_flush_reset(), paint_screen(), paint_screen_backdrop()
+ * add: persistent gauge API: bsddialog_gauge_open(),
+ bsddialog_gauge_update(), bsddialog_gauge_close(),
+ bsddialog_gauge_flush()
+ * add: dialog_repaint_frame(), dialog_repaint_text(), dialog_place(),
+ dialog_paint_frame(), textpad_to_widget(), and
+ bsddialog_set_default_theme_noflush()
+ * add: KEY_BTAB (Shift+Tab) backward focus navigation in all dialogs
+ with buttons or multi-pane focus (form, menu, calendar, slider,
+ timebox, datebox, msgbox, textbox, rangebox, pause, ...)
+ * add: BUTTONS_TAB_FORWARD() and BUTTONS_TAB_BACKWARD() helpers
+ * fix: scrollback pollution: prefer werase(3) over wclear(3); paint the
+ shadow as an L-shape instead of filling the whole shadow window;
+ untouchwin(3) stdscr(3) before doupdate(3) so bkgd(3) does not emit
+ clear_screen (CSI-H CSI-2J) into scrollback
+ * fix: textpad copywin(3) off-by-one (stray reverse-video cell after a
+ full-width line); rtextpad(3) width now matches the textpad; erase the
+ widget text band before copywin(3); print_textpad(3) newline/space
+ handling at pad width
+ * fix: calendar opens with the day grid focused instead of the buttons
+ * fix: correct noeol files bsddialog_progressview.h bsddialog_theme.h
+ datebox.c lib_util.c libbsddialog.c
+ * change: bsddialog_backtitle_rf() uses dialog_flush_stdscr()
+ * change: theme install no longer wnoutrefresh(3) stdscr(3) immediately;
+ bsddialog_get_theme() copies colours without applying bkgd(3)
+ * change: replace refresh()/doupdate() call sites with the new flush
+ helpers throughout the library
+
+ Examples (library):
+ * add: clean
+ * fix: compile
+ - In FreeBSD tree OBJDIR precludes use of libdir=../lib
+ - Remap libdir to system library path when appropriate
+ - -I$libdir is confusing; use -I$incdir instead
+ - Remove unnecessary brackets in ${e}.c
+
+ Examples (utility):
+ * change: gauge.sh use awk(1) NF built-in instead of split() return
+ * change: gauge.sh and mixedgauge.sh use $(( ... )) instead of expr(1)
+ * change: pause.sh and rangebox.sh use one extra row (7 → 8); show text
+ * change: mark calendar.sh, rangebox.sh, and slider.sh executable
+
2025-08-15 Version 1.1
Library:
diff --git a/contrib/bsddialog/examples_library/clean b/contrib/bsddialog/examples_library/clean
new file mode 100755
--- /dev/null
+++ b/contrib/bsddialog/examples_library/clean
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+#
+set -x
+rm -f menu checklist radiolist mixedlist theme infobox yesno msgbox datebox \
+ form timebox rangebox pause calendar gauge mixedgauge textbox slider
diff --git a/contrib/bsddialog/examples_library/compile b/contrib/bsddialog/examples_library/compile
--- a/contrib/bsddialog/examples_library/compile
+++ b/contrib/bsddialog/examples_library/compile
@@ -10,14 +10,23 @@
set -x
+library=bsddialog
libpath=../lib
+incpath=$libpath
+if [ ! -e "$libpath/lib$library.so" ]; then
+ # In FreeBSD tree, library is in architecture dependent OBJDIR
+ # Compile against system-installed copy when operating in-tree
+ library=privatebsddialog
+ libpath=/usr/lib
+ incpath=/usr/include/private/bsddialog
+fi
examples="menu checklist radiolist mixedlist theme infobox yesno msgbox \
datebox form timebox rangebox pause calendar gauge mixedgauge textbox slider"
-rm -f $examples
+./clean
for e in $examples
do
- cc -g -Wall -Wextra -I$libpath ${e}.c -o $e \
- -Wl,-rpath=$libpath -L$libpath -lbsddialog
+ cc -g -Wall -Wextra -I$incpath $e.c -o $e \
+ -Wl,-rpath=$libpath -L$libpath -l$library
done
diff --git a/contrib/bsddialog/examples_utility/gauge.sh b/contrib/bsddialog/examples_utility/gauge.sh
--- a/contrib/bsddialog/examples_utility/gauge.sh
+++ b/contrib/bsddialog/examples_utility/gauge.sh
@@ -10,13 +10,13 @@
# <http://creativecommons.org/publicdomain/zero/1.0/>.
characters="A B C D E F G"
-total=`echo $characters | awk '{print split($0, a)}'`
+total=$( echo $characters | awk '{print NF}' )
i=1
for c in $characters
do
sleep 1
echo XXX
- echo "$(expr $(expr $i "*" 100) "/" $total)"
+ echo "$(( ($i * 100) / $total ))"
echo "[$i/$total] Char: $c"
echo XXX
if [ $i -eq $total ]
@@ -24,5 +24,5 @@
sleep 1
echo EOF
fi
- i=`expr $i + 1`
+ i=$(( $i + 1 ))
done | ./bsddialog --title " gauge " --gauge "[0/$total] Starting..." 10 70
diff --git a/contrib/bsddialog/examples_utility/mixedgauge.sh b/contrib/bsddialog/examples_utility/mixedgauge.sh
--- a/contrib/bsddialog/examples_utility/mixedgauge.sh
+++ b/contrib/bsddialog/examples_utility/mixedgauge.sh
@@ -28,6 +28,6 @@
"Label 11" " -11" \
"Label X" $perc
- perc=`expr $perc + 20`
- mainperc=`expr $mainperc + 10`
+ perc=$(( $perc + 20 ))
+ mainperc=$(( $mainperc + 10 ))
done
diff --git a/contrib/bsddialog/examples_utility/pause.sh b/contrib/bsddialog/examples_utility/pause.sh
--- a/contrib/bsddialog/examples_utility/pause.sh
+++ b/contrib/bsddialog/examples_utility/pause.sh
@@ -15,7 +15,7 @@
: ${BSDDIALOG_TIMEOUT=4}
: ${BSDDIALOG_ESC=5}
-./bsddialog --title " pause " --pause "Hello World!" 7 35 10
+./bsddialog --title " pause " --pause "Hello World!" 8 35 10
case $? in
$BSDDIALOG_ERROR )
diff --git a/contrib/bsddialog/examples_utility/rangebox.sh b/contrib/bsddialog/examples_utility/rangebox.sh
--- a/contrib/bsddialog/examples_utility/rangebox.sh
+++ b/contrib/bsddialog/examples_utility/rangebox.sh
@@ -14,7 +14,7 @@
: ${BSDDIALOG_CANCEL=1}
: ${BSDDIALOG_ESC=5}
-VALUE=$(./bsddialog --title " rangebox " --rangebox "Hello World!" 7 35 0 10 5 \
+VALUE=$(./bsddialog --title " rangebox " --rangebox "Hello World!" 8 35 0 10 5 \
3>&1 1>&2 2>&3 3>&-)
case $? in
diff --git a/contrib/bsddialog/lib/barbox.c b/contrib/bsddialog/lib/barbox.c
--- a/contrib/bsddialog/lib/barbox.c
+++ b/contrib/bsddialog/lib/barbox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -76,6 +77,72 @@
int label; /* rangebox and pause perc!=label */
};
+static struct {
+ bool active;
+ bool sized;
+ struct dialog d;
+} pgauge;
+
+static void draw_bar(struct bar *b);
+static void update_barbox(struct dialog *d, struct bar *b, bool buttons);
+static int gauge_draw_bar_in_widget(struct dialog *d, unsigned int perc);
+
+static int
+gauge_repaint(unsigned int perc, bool resize)
+{
+ perc = MIN(perc, 100);
+
+ if (resize) {
+ if (dialog_size_position(&pgauge.d, HBOX, MIN_WBOX, NULL) != 0)
+ return (BSDDIALOG_ERROR);
+ if (dialog_place(&pgauge.d) != 0)
+ return (BSDDIALOG_ERROR);
+ pgauge.sized = true;
+ } else if (!pgauge.sized)
+ RETURN_ERROR("Gauge is not sized");
+
+ if (dialog_paint_frame(&pgauge.d) != 0)
+ return (BSDDIALOG_ERROR);
+ if (dialog_repaint_text(&pgauge.d, 0, 0, 0, HBOX) != 0)
+ return (BSDDIALOG_ERROR);
+ if (gauge_draw_bar_in_widget(&pgauge.d, perc) != 0)
+ return (BSDDIALOG_ERROR);
+
+ dialog_backdrop_sync();
+ if (pgauge.d.conf->shadow && pgauge.d.shadow != NULL) {
+ touchwin(pgauge.d.shadow);
+ wnoutrefresh(pgauge.d.shadow);
+ }
+ touchwin(pgauge.d.widget);
+ wnoutrefresh(pgauge.d.widget);
+
+ return (BSDDIALOG_OK);
+}
+
+static int
+gauge_draw_bar_in_widget(struct dialog *d, unsigned int perc)
+{
+ struct bar b;
+ WINDOW *bbox;
+
+ bbox = derwin(d->widget, HBOX, WBOX(d), d->h - BORDER - HBOX,
+ BORDER + BARPADDING);
+ if (bbox == NULL)
+ RETURN_ERROR("Cannot build gauge bar");
+
+ draw_borders(d->conf, bbox, RAISED);
+ b.win = bbox;
+ b.y = b.x = 1;
+ b.w = WBAR(d);
+ b.fmt = "%3d%%";
+ b.perc = b.label = MIN((int)perc, 100);
+ b.toupdate = false;
+ draw_bar(&b);
+ delwin(bbox);
+
+ return (BSDDIALOG_OK);
+}
+
static void draw_bar(struct bar *b)
{
int barlen, xlabel;
@@ -114,17 +181,88 @@
WBOX(d), RAISED);
}
+int
+bsddialog_gauge_open(struct bsddialog_conf *conf, const char *text, int rows,
+ int cols, unsigned int perc)
+{
+ if (pgauge.active)
+ bsddialog_gauge_close();
+
+ if (prepare_dialog(conf, text, rows, cols, &pgauge.d) != 0)
+ return (BSDDIALOG_ERROR);
+ scrollok(stdscr, FALSE);
+ leaveok(stdscr, TRUE);
+ leaveok(pgauge.d.widget, TRUE);
+ syncok(pgauge.d.widget, TRUE);
+ if (pgauge.d.conf->shadow && pgauge.d.shadow != NULL) {
+ leaveok(pgauge.d.shadow, TRUE);
+ syncok(pgauge.d.shadow, TRUE);
+ }
+ pgauge.active = true;
+ pgauge.sized = false;
+
+ return (gauge_repaint(perc, true));
+}
+
+int
+bsddialog_gauge_update(const char *text, int rows, int cols,
+ unsigned int perc)
+{
+ if (!pgauge.active)
+ RETURN_ERROR("Gauge is not open");
+
+ pgauge.d.text = CHECK_STR(text);
+ (void)rows;
+ (void)cols;
+
+ return (gauge_repaint(perc, false));
+}
+
+void
+bsddialog_gauge_close(void)
+{
+ if (!pgauge.active)
+ return;
+
+ if (bsddialog_inmode())
+ end_dialog(&pgauge.d);
+ memset(&pgauge, 0, sizeof(pgauge));
+}
+
+void
+bsddialog_gauge_flush(void)
+{
+ if (!pgauge.active || !pgauge.sized)
+ return;
+
+ if (pgauge.d.conf->shadow && pgauge.d.shadow != NULL) {
+ touchwin(pgauge.d.shadow);
+ wnoutrefresh(pgauge.d.shadow);
+ }
+ touchwin(pgauge.d.widget);
+ wnoutrefresh(pgauge.d.widget);
+ untouchwin(stdscr);
+ doupdate();
+}
+
int
bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int perc, int fd, const char *sep, const char *end)
{
bool mainloop;
- int fd2;
+ int fd2, retval;
FILE *input;
char inputbuf[2048], ntext[2048], *pntext;
struct bar b;
struct dialog d;
+ if (fd < 0) {
+ retval = bsddialog_gauge_open(conf, text, rows, cols, perc);
+ if (retval == BSDDIALOG_OK)
+ bsddialog_gauge_close();
+ return (retval);
+ }
+
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
if ((b.win = newwin(1, 1, 1, 1)) == NULL)
@@ -147,21 +285,19 @@
while (mainloop) {
if (d.built) {
hide_dialog(&d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(&d))
return (BSDDIALOG_ERROR);
- if (d.built)
- refresh(); /* fix grey lines expanding screen */
TEXTPAD(&d, HBOX);
update_barbox(&d, &b, false);
b.w = WBAR(&d);
b.perc = b.label = perc;
b.toupdate = true;
draw_bar(&b);
- doupdate();
+ dialog_flush_overlay();
if (input == NULL) /* that is fd < 0 */
break;
@@ -339,7 +475,7 @@
b.toupdate = true;
draw_bar(&b);
- doupdate();
+ dialog_flush();
/* getch(); to test with "alternate mode" */
delwin(b.win);
@@ -410,7 +546,8 @@
bsddialog_total_progview / (float)(tnew - tstart);
printw(pvconf->fmtbottomstr, bsddialog_total_progview,
readforsec);
- refresh();
+ wnoutrefresh(stdscr);
+ dialog_flush_stdscr();
time(&told);
update = false;
@@ -445,16 +582,15 @@
{
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
-
b->w = WBAR(d);
*bigchange = MAX(1, b->w / 10);
update_barbox(d, b, true);
@@ -501,7 +637,7 @@
b.label = currvalue;
draw_bar(&b);
}
- doupdate();
+ dialog_flush();
if (get_wch(&input) == ERR)
continue;
switch(input) {
@@ -522,6 +658,7 @@
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_CTRL('p'):
case KEY_LEFT:
d.bs.curr--;
@@ -580,7 +717,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
loop = false;
}
@@ -599,16 +736,15 @@
{
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
-
b->w = WBAR(d);
update_barbox(d, b, true);
b->toupdate = true;
@@ -647,7 +783,7 @@
b.label = tout;
draw_bar(&b);
}
- doupdate();
+ dialog_flush();
if (get_wch(&input) == ERR) { /* timeout */
tout--;
if (tout < 0) {
@@ -676,6 +812,7 @@
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_LEFT:
d.bs.curr--;
if (d.bs.curr < 0)
@@ -699,7 +836,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
loop = false;
}
diff --git a/contrib/bsddialog/lib/bsddialog.h b/contrib/bsddialog/lib/bsddialog.h
--- a/contrib/bsddialog/lib/bsddialog.h
+++ b/contrib/bsddialog/lib/bsddialog.h
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,7 +31,7 @@
#include <stdbool.h>
-#define LIBBSDDIALOG_VERSION "1.1"
+#define LIBBSDDIALOG_VERSION "1.2"
/* Return values */
#define BSDDIALOG_ERROR -1
@@ -176,6 +177,9 @@
int bsddialog_init(void);
int bsddialog_init_notheme(void);
+void bsddialog_keep_tite(void);
+void bsddialog_fill_backdrop(void);
+void bsddialog_paint_screen(void);
bool bsddialog_inmode(void);
int bsddialog_end(void);
int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle);
@@ -208,6 +212,20 @@
bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int perc, int fd, const char *sep, const char *end);
+int
+bsddialog_gauge_open(struct bsddialog_conf *conf, const char *text, int rows,
+ int cols, unsigned int perc);
+
+int
+bsddialog_gauge_update(const char *text, int rows, int cols,
+ unsigned int perc);
+
+void
+bsddialog_gauge_close(void);
+
+void
+bsddialog_gauge_flush(void);
+
int
bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
int cols);
diff --git a/contrib/bsddialog/lib/bsddialog_progressview.h b/contrib/bsddialog/lib/bsddialog_progressview.h
--- a/contrib/bsddialog/lib/bsddialog_progressview.h
+++ b/contrib/bsddialog/lib/bsddialog_progressview.h
@@ -57,4 +57,4 @@
int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
struct bsddialog_fileminibar *minibar);
-#endif
\ No newline at end of file
+#endif
diff --git a/contrib/bsddialog/lib/bsddialog_theme.h b/contrib/bsddialog/lib/bsddialog_theme.h
--- a/contrib/bsddialog/lib/bsddialog_theme.h
+++ b/contrib/bsddialog/lib/bsddialog_theme.h
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -119,6 +120,7 @@
int bsddialog_get_theme(struct bsddialog_theme *theme);
bool bsddialog_hascolors(void);
int bsddialog_set_default_theme(enum bsddialog_default_theme theme);
+int bsddialog_set_default_theme_noflush(enum bsddialog_default_theme theme);
int bsddialog_set_theme(struct bsddialog_theme *theme);
-#endif
\ No newline at end of file
+#endif
diff --git a/contrib/bsddialog/lib/datebox.c b/contrib/bsddialog/lib/datebox.c
--- a/contrib/bsddialog/lib/datebox.c
+++ b/contrib/bsddialog/lib/datebox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -251,6 +252,7 @@
if (focus)
wattroff(win, t.menu.f_namecolor);
+ touchwin(win);
wnoutrefresh(win);
}
@@ -261,7 +263,12 @@
int ndays, i, y, x, wd, h, w;
getmaxyx(win, h, w);
- wclear(win);
+ /*
+ * werase(3), not wclear(3): wclear(3) sets win->_clear and the next
+ * wnoutrefresh(3) forces NewScreen(3)->_clear, so doupdate(3) emits
+ * clear_screen (CSI-H CSI-2J) into scrollback on every key.
+ */
+ werase(win);
draw_borders(conf, win, RAISED);
if (active) {
wattron(win, t.dialog.arrowcolor);
@@ -295,6 +302,15 @@
wnoutrefresh(win);
}
+static void
+calendar_winopts(WINDOW *win)
+{
+
+ leaveok(win, FALSE);
+ scrollok(win, FALSE);
+ idlok(win, FALSE);
+}
+
static int
calendar_draw(struct dialog *d, bool redraw, WINDOW *yy_win, WINDOW *mm_win,
WINDOW *dd_win)
@@ -303,14 +319,14 @@
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, MINHCAL + HBUTTONS);
ycal = d->y + d->h - 15;
@@ -321,6 +337,7 @@
update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED);
update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED);
wnoutrefresh(d->widget);
+ dialog_flush();
return (0);
}
@@ -348,22 +365,29 @@
if ((yy_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for yy");
wbkgd(yy_win, t.dialog.color);
+ calendar_winopts(yy_win);
if ((mm_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for mm");
wbkgd(mm_win, t.dialog.color);
+ calendar_winopts(mm_win);
if ((dd_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for dd");
wbkgd(dd_win, t.dialog.color);
+ calendar_winopts(dd_win);
if (calendar_draw(&d, false, yy_win, mm_win, dd_win) != 0)
return (BSDDIALOG_ERROR);
- sel = -1;
- loop = focusbuttons = true;
+ sel = 2;
+ focusbuttons = false;
+ d.bs.curr = conf->button.always_active ? 0 : -1;
+ loop = true;
while (loop) {
+ draw_buttons(&d);
+ wnoutrefresh(d.widget);
drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0);
drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1);
print_calendar(conf, dd_win, yy, mm, dd, sel == 2);
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
@@ -400,6 +424,25 @@
}
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
+ if (focusbuttons) {
+ d.bs.curr--;
+ if (d.bs.curr < 0) {
+ focusbuttons = false;
+ sel = 2;
+ d.bs.curr = conf->button.always_active ?
+ 0 : -1;
+ }
+ } else {
+ sel--;
+ if (sel < 0) {
+ focusbuttons = true;
+ sel = -1;
+ d.bs.curr = (int)d.bs.nbuttons - 1;
+ }
+ }
+ DRAW_BUTTONS(d);
+ break;
case KEY_CTRL('n'):
case KEY_RIGHT:
if (focusbuttons) {
@@ -454,7 +497,10 @@
break;
case KEY_DOWN:
if (focusbuttons) {
- break;
+ sel = 2;
+ focusbuttons = false;
+ d.bs.curr = conf->button.always_active ? 0 : -1;
+ DRAW_BUTTONS(d);
} else if (sel == 0) {
datectl(DOWN_MONTH, &yy, &mm, &dd);
} else if (sel == 1) {
@@ -514,7 +560,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
loop = false;
}
@@ -539,14 +585,14 @@
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, 3 /*windows*/ + HBUTTONS);
y = d->y + d->h - 6;
@@ -633,7 +679,7 @@
for (i = 0; i < 3; i++)
drawsquare(conf, di[i].win, LOWERED, di[i].fmt,
*di[i].value, sel == i);
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
@@ -672,6 +718,7 @@
}
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_CTRL('p'):
case KEY_LEFT:
if (focusbuttons) {
@@ -706,9 +753,13 @@
break;
case '+':
case KEY_DOWN:
- if (focusbuttons)
- break;
- datectl(di[sel].down, &yy, &mm, &dd);
+ if (focusbuttons) {
+ sel = 2;
+ focusbuttons = false;
+ d.bs.curr = conf->button.always_active ? 0 : -1;
+ DRAW_BUTTONS(d);
+ } else
+ datectl(di[sel].down, &yy, &mm, &dd);
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
@@ -727,7 +778,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
loop = false;
}
@@ -743,4 +794,4 @@
end_dialog(&d);
return (retval);
-}
\ No newline at end of file
+}
diff --git a/contrib/bsddialog/lib/formbox.c b/contrib/bsddialog/lib/formbox.c
--- a/contrib/bsddialog/lib/formbox.c
+++ b/contrib/bsddialog/lib/formbox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -500,7 +501,8 @@
attron(t.form.bottomdesccolor);
addstr(item->bottomdesc);
attroff(t.form.bottomdesccolor);
- refresh();
+ wnoutrefresh(stdscr);
+ dialog_flush_stdscr();
}
}
@@ -607,7 +609,7 @@
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
f->viewrows = f->formheight;
f->w = f->wmin;
@@ -616,7 +618,7 @@
if (draw_dialog(d) != 0) /* doupdate() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, 2 /* box borders */ + f->viewrows + HBUTTONS);
update_box(d->conf, f->box, d->y + d->h - 5 - f->viewrows, d->x + 2,
@@ -674,7 +676,7 @@
int cols, unsigned int formheight, unsigned int nitems,
struct bsddialog_formitem *items, int *focusitem)
{
- bool switchfocus, changeitem, focusinform, loop;
+ bool switchfocus, switchfocus_last, changeitem, focusinform, loop;
int next, retval, wchtype;
unsigned int i;
wint_t input;
@@ -710,10 +712,10 @@
if (form_draw(&d, false, &form, focusinform) != 0)
return (BSDDIALOG_ERROR);
- changeitem = switchfocus = false;
+ changeitem = switchfocus = switchfocus_last = false;
loop = true;
while (loop) {
- doupdate();
+ dialog_flush_overlay();
if ((wchtype = get_wch(&input)) == ERR)
continue;
switch(input) {
@@ -757,6 +759,27 @@
wnoutrefresh(d.widget);
}
break;
+ case KEY_BTAB: /* Shift+TAB */
+ if (focusinform) {
+ next = previtem(form.nitems, form.pritems,
+ form.sel);
+ if (next < form.sel)
+ changeitem = true;
+ else {
+ switchfocus = true;
+ switchfocus_last = true;
+ }
+ } else {
+ if (d.bs.curr > 0) {
+ d.bs.curr--;
+ redrawbuttons(&d, true, true);
+ wnoutrefresh(d.widget);
+ } else if (form.sel != -1) {
+ switchfocus = true;
+ switchfocus_last = true;
+ }
+ }
+ break;
case KEY_LEFT:
if (focusinform) {
if (fieldctl(item, MOVE_CURSOR_LEFT))
@@ -880,7 +903,7 @@
} else {
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush_widget(&d);
retval = BUTTONVALUE(d.bs);
loop = false;
}
@@ -890,7 +913,15 @@
if (switchfocus) {
focusinform = !focusinform;
- d.bs.curr = 0;
+ if (focusinform == false) {
+ d.bs.curr = switchfocus_last ?
+ (int)d.bs.nbuttons - 1 : 0;
+ } else {
+ d.bs.curr = conf->button.always_active ? 0 : -1;
+ next = switchfocus_last ?
+ lastitem(form.nitems, form.pritems) :
+ firstitem(form.nitems, form.pritems);
+ }
redrawbuttons(&d,
conf->button.always_active || !focusinform,
!focusinform);
@@ -898,13 +929,13 @@
if (focusinform == false)
DRAWITEM_TRICK(&form, form.sel, false);
else {
- next = firstitem(form.nitems, form.pritems);
if (next == form.sel)
DRAWITEM_TRICK(&form, form.sel, true);
else
changeitem = true;
}
switchfocus = false;
+ switchfocus_last = false;
}
if (changeitem) {
diff --git a/contrib/bsddialog/lib/lib_util.h b/contrib/bsddialog/lib/lib_util.h
--- a/contrib/bsddialog/lib/lib_util.h
+++ b/contrib/bsddialog/lib/lib_util.h
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -68,6 +69,8 @@
/* widget utils */
#define KEY_CTRL(c) (c & 037)
#define TEXTPAD(d, downnotext) rtextpad(d, 0, 0, 0, downnotext)
+#define TEXTPAD_TO_WIDGET(d, downnotext) \
+ textpad_to_widget(d, 0, 0, 0, downnotext)
#define SCREENLINES (getmaxy(stdscr))
#define SCREENCOLS (getmaxx(stdscr))
#define CHECK_STR(s) (s == NULL ? "" : s)
@@ -76,8 +79,16 @@
#define LARROW(c) (c->ascii_lines ? '<' : ACS_LARROW)
#define RARROW(c) (c->ascii_lines ? '>' : ACS_RARROW)
#define DRAW_BUTTONS(d) do { \
- draw_buttons(&d); \
- wnoutrefresh(d.widget); \
+ draw_buttons(&(d)); \
+ wnoutrefresh((d).widget); \
+} while (0)
+#define BUTTONS_TAB_FORWARD(bs) do { \
+ (bs).curr = ((bs).curr + 1) % (int)(bs).nbuttons; \
+} while (0)
+#define BUTTONS_TAB_BACKWARD(bs) do { \
+ (bs).curr--; \
+ if ((bs).curr < 0) \
+ (bs).curr = (int)(bs).nbuttons - 1; \
} while (0)
/* internal types */
@@ -128,6 +139,19 @@
/* widget utils */
int hide_dialog(struct dialog *d);
+void dialog_flush_reset(void);
+void dialog_backdrop_sync(void);
+void dialog_flush(void);
+void dialog_flush_widget(struct dialog *d);
+void dialog_flush_overlay(void);
+void dialog_flush_stdscr(void);
+void dialog_backtitle_set(struct bsddialog_conf *conf, const char *backtitle);
+void dialog_backtitle_paint(void);
+void dialog_backtitle_clear(void);
+void paint_screen(chtype attr);
+void paint_screen_backdrop(void);
+void bsddialog_cancel_alternate_screen(void);
+void bsddialog_enter_alternate_screen(void);
int f1help_dialog(struct bsddialog_conf *conf);
void
@@ -140,6 +164,10 @@
void
rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext);
+void
+textpad_to_widget(struct dialog *d, int ytext, int xtext, int upnotext,
+ int downnotext);
+
/* (auto) sizing and (auto) position */
int
set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h,
@@ -160,6 +188,11 @@
/* dialog */
void end_dialog(struct dialog *d);
int draw_dialog(struct dialog *d);
+int dialog_repaint_frame(struct dialog *d);
+int dialog_place(struct dialog *d);
+int dialog_paint_frame(struct dialog *d);
+int dialog_repaint_text(struct dialog *d, int ytext, int xtext, int upnotext,
+ int downnotext);
int
prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows,
diff --git a/contrib/bsddialog/lib/lib_util.c b/contrib/bsddialog/lib/lib_util.c
--- a/contrib/bsddialog/lib/lib_util.c
+++ b/contrib/bsddialog/lib/lib_util.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,6 +28,7 @@
#include <stdarg.h>
#include <curses.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -36,6 +38,11 @@
#include "bsddialog_theme.h"
#include "lib_util.h"
+#include <term.h>
+#ifdef buttons
+#undef buttons
+#endif
+
/*
* -1- Error and diagnostic
*
@@ -937,6 +944,211 @@
/*
* -5- Widget components and utilities
*/
+static chtype
+screen_backdrop(void)
+{
+
+ return (t.screen.color);
+}
+
+void
+paint_screen(chtype attr)
+{
+ int col, row;
+
+ bkgd(attr);
+ for (row = 0; row < SCREENLINES; row++) {
+ for (col = 0; col < SCREENCOLS; col++)
+ mvwaddch(stdscr, row, col, ' ' | attr);
+ }
+}
+
+void
+paint_screen_backdrop(void)
+{
+
+ paint_screen(screen_backdrop());
+}
+
+void
+bsddialog_cancel_alternate_screen(void)
+{
+ /*
+ * Leave alternate-screen buffer without refresh(3) or clear_screen.
+ * refresh(3) here would push a blank frame into scrollback;
+ * clear_screen would flush full-screen repaint before gauge exists.
+ */
+ if ((!isatty(fileno(stdout)) && !isatty(fileno(stderr))) ||
+ exit_ca_mode == NULL || exit_ca_mode[0] == '\0')
+ return;
+
+ (void)putp(exit_ca_mode);
+ enter_ca_mode = NULL;
+ exit_ca_mode = NULL;
+}
+
+void
+bsddialog_enter_alternate_screen(void)
+{
+
+ /*
+ * Draw on the alternate-screen buffer so ncurses home+clear sequences
+ * (CSI-H CSI-2J) never append to scrollback. bsdpv(3) uses
+ * bsddialog_init_notheme(3) and optionally bsddialog_keep_tite(3)
+ * instead.
+ */
+ if ((!isatty(fileno(stdout)) && !isatty(fileno(stderr))) ||
+ enter_ca_mode == NULL || enter_ca_mode[0] == '\0')
+ return;
+
+ (void)putp(enter_ca_mode);
+}
+
+static bool backdrop_synced;
+
+/*
+ * Backtitle is drawn on stdscr(3). dialog_backdrop_sync(3) fills the whole
+ * screen after bsddialog_backtitle(3) runs (utility order), so remember the
+ * text and repaint it after every full backdrop fill.
+ */
+static char *backtitle_text;
+static bool backtitle_ascii_lines;
+static bool backtitle_no_lines;
+static bool backtitle_active;
+
+void
+dialog_backtitle_clear(void)
+{
+
+ free(backtitle_text);
+ backtitle_text = NULL;
+ backtitle_ascii_lines = false;
+ backtitle_no_lines = false;
+ backtitle_active = false;
+}
+
+void
+dialog_backtitle_set(struct bsddialog_conf *conf, const char *backtitle)
+{
+
+ dialog_backtitle_clear();
+ if (conf == NULL || backtitle == NULL)
+ return;
+ backtitle_text = strdup(backtitle);
+ if (backtitle_text == NULL)
+ return;
+ backtitle_ascii_lines = conf->ascii_lines;
+ backtitle_no_lines = conf->no_lines;
+ backtitle_active = true;
+}
+
+void
+dialog_backtitle_paint(void)
+{
+
+ if (!backtitle_active || backtitle_text == NULL)
+ return;
+
+ move(0, 1);
+ clrtoeol();
+ addstr(backtitle_text);
+ if (backtitle_no_lines != true) {
+ if (backtitle_ascii_lines)
+ mvhline(1, 1, '-', SCREENCOLS - 2);
+ else
+ mvhline_set(1, 1, WACS_HLINE, SCREENCOLS - 2);
+ }
+}
+
+void
+dialog_backdrop_sync(void)
+{
+
+ if (backdrop_synced)
+ return;
+ /*
+ * bkgd(3) at theme init only sets the attribute;
+ * paint_screen_backdrop(3) fills stdscr(3) in memory. wnoutrefresh(3)
+ * stdscr(3) must run before any other wnoutrefresh(3) in the same
+ * frame; otherwise the sparse stdscr merge wipes dialog windows
+ * already queued (calendar(3) would lose box while the blue backdrop
+ * returned).
+ *
+ * Full-screen fill also erases any prior bsddialog_backtitle(3); paint
+ * the remembered backtitle back onto the top lines before refresh.
+ */
+ paint_screen_backdrop();
+ dialog_backtitle_paint();
+ wnoutrefresh(stdscr);
+ /*
+ * Leave the backdrop in newscr(3) but do not keep stdscr(3) touched:
+ * bkgd(3) on a touched stdscr(3) forces clear_screen into scrollback
+ * on the next doupdate(3) (see bsddialog_fill_backdrop(3)).
+ */
+ untouchwin(stdscr);
+ backdrop_synced = true;
+}
+
+void
+dialog_flush_reset(void)
+{
+
+ backdrop_synced = false;
+}
+
+void
+dialog_flush(void)
+{
+
+ /*
+ * Match bsddialog_gauge_flush(3): untouchwin(3) stdscr(3) before
+ * doupdate(3) so bkgd(3) does not force clear_screen (CSI-H CSI-2J)
+ * into scrollback. Sync backdrop with dialog_backdrop_sync(3) from
+ * draw_dialog(3) before queueing widget damage.
+ */
+ untouchwin(stdscr);
+ doupdate();
+ untouchwin(stdscr);
+}
+
+void
+dialog_flush_widget(struct dialog *d)
+{
+
+ if (d->conf->shadow && d->shadow != NULL) {
+ touchwin(d->shadow);
+ wnoutrefresh(d->shadow);
+ }
+ touchwin(d->widget);
+ wnoutrefresh(d->widget);
+ dialog_flush();
+}
+
+void
+dialog_flush_overlay(void)
+{
+
+ /*
+ * Flush after wnoutrefresh(3) on separate newwin(3) layers placed above
+ * d->widget (calendar day grid, menu box, slider controls, ...). Do not
+ * wnoutrefresh(3) the widget here (that merge would paint over them).
+ */
+ dialog_flush();
+}
+
+void
+dialog_flush_stdscr(void)
+{
+
+ /*
+ * Flush after an explicit wnoutrefresh(3) on stdscr(3) (backtitle,
+ * bottom description). Do not untouchwin(3) before doupdate(3).
+ */
+ doupdate();
+ if (backdrop_synced)
+ untouchwin(stdscr);
+}
+
int hide_dialog(struct dialog *d)
{
WINDOW *clear;
@@ -1028,7 +1240,9 @@
update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w,
enum elevation elev)
{
- wclear(win);
+ /* werase(3): see print_calendar(3) in datebox.c
+ * (wclear(3) floods scrollback) */
+ werase(win);
wresize(win, h, w);
mvwin(win, y, x);
draw_borders(conf, win, elev);
@@ -1037,11 +1251,46 @@
void
rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext)
{
+ int dmaxcol, dmincol;
+
+ dmincol = BORDER + TEXTHMARGIN;
+ dmaxcol = dmincol + getmaxx(d->textpad) - xtext - 1;
pnoutrefresh(d->textpad, ytext, xtext,
d->y + BORDER + upnotext,
- d->x + BORDER + TEXTHMARGIN,
+ d->x + dmincol,
d->y + d->h - 1 - downnotext - BORDER,
- d->x + d->w - TEXTHMARGIN - BORDER);
+ d->x + dmaxcol);
+}
+
+void
+textpad_to_widget(struct dialog *d, int ytext, int xtext, int upnotext,
+ int downnotext)
+{
+ int dmaxcol, dmaxrow, dmincol, dminrow, row;
+
+ dminrow = BORDER + upnotext;
+ dmincol = BORDER + TEXTHMARGIN;
+ dmaxrow = d->h - 1 - downnotext - BORDER;
+ /*
+ * Match the textpad width (d->w - BORDERS - TEXTHMARGINS). Using
+ * d->w - TEXTHMARGIN - BORDER reads one source column past the pad;
+ * copywin(3) then pulls garbage/reverse-video onto the widget row.
+ */
+ dmaxcol = dmincol + getmaxx(d->textpad) - xtext - 1;
+
+ /*
+ * Erase the widget text band before copywin(3). Overlay FALSE skips
+ * blank source cells; without this, reverse-video residue can remain
+ * when a prompt line shrinks (e.g. unbounded read-count vs pct bar).
+ */
+ wattrset(d->widget, t.dialog.color);
+ for (row = dminrow; row <= dmaxrow; row++)
+ mvwhline(d->widget, row, dmincol, ' ',
+ dmaxcol - dmincol + 1);
+
+ (void)copywin(d->textpad, d->widget, ytext, xtext, dminrow, dmincol,
+ dmaxrow, dmaxcol, false);
+ wattrset(d->widget, t.dialog.color);
}
/*
@@ -1081,14 +1330,13 @@
switch (wtext[2]) {
case L'n':
- wattron(win, t.dialog.color);
- wattrset(win, A_NORMAL);
+ wattrset(win, t.dialog.color);
break;
case L'b':
- wattron(win, A_BOLD);
+ wattrset(win, t.dialog.color | A_BOLD);
break;
case L'B':
- wattroff(win, A_BOLD);
+ wattrset(win, t.dialog.color);
break;
case L'd':
wattron(win, A_DIM);
@@ -1213,6 +1461,19 @@
loop = false;
break;
case L'\n':
+ /*
+ * When the line fills the pad width (x == cols),
+ * wmove(3) to x and wclrtoeol(3) touch column cols
+ * (one past the last valid cell) leaving a reverse-
+ * video blank that copywin(3) later copies onto the
+ * widget (stray black cell after a full-width read-
+ * count bracket).
+ */
+ if (x < cols) {
+ wmove(pad, y, x);
+ wattrset(pad, t.dialog.color);
+ wclrtoeol(pad);
+ }
x = 0;
y++;
j = -1;
@@ -1228,6 +1489,8 @@
j = -1;
break;
case L' ':
+ wmove(pad, y, x);
+ mvwaddwch(pad, y, x, L' ');
x++;
if (x >= cols) {
x = 0;
@@ -1245,20 +1508,81 @@
i++;
}
+ wattrset(pad, t.dialog.color);
+
free(wtext);
free(string);
return (0);
}
-int draw_dialog(struct dialog *d)
+static void
+paint_shadow_lshape(WINDOW *win, int h, int w)
+{
+ int col, row, x0, y0;
+
+ if (win == NULL || h <= 0 || w <= 0)
+ return;
+
+ /*
+ * The shadow window is the same size as the widget but offset
+ * down/right by t.shadow.y/t.shadow.x. Only the bottom band and right
+ * band of this window fall outside the widget on screen; the rest
+ * overlaps widget interior. Leave those cells blank so wnoutrefresh(3)
+ * does not paint black over the widget (e.g. a stray cell after full-
+ * width prompt).
+ */
+ y0 = h - (int)t.shadow.y;
+ if (y0 < 0)
+ y0 = 0;
+ x0 = w - (int)t.shadow.x;
+ if (x0 < 0)
+ x0 = 0;
+
+ /*
+ * werase(3), not wclear(3): wclear(3) sets win->_clear and the next
+ * wnoutrefresh(3) forces NewScreen(3)->_clear, so doupdate(3) emits
+ * clear_screen (CSI-H CSI-2J); a full blue backdrop page in scrollback
+ * before dialog is painted.
+ */
+ werase(win);
+ for (row = y0; row < h; row++) {
+ for (col = 0; col < w; col++)
+ mvwaddch(win, row, col, ' ' | t.shadow.color);
+ }
+ for (row = 0; row < h; row++) {
+ for (col = x0; col < w; col++)
+ mvwaddch(win, row, col, ' ' | t.shadow.color);
+ }
+}
+
+int
+dialog_place(struct dialog *d)
+{
+
+ if (d->conf->shadow) {
+ wresize(d->shadow, d->h, d->w);
+ mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x);
+ wbkgd(d->shadow, t.shadow.color);
+ paint_shadow_lshape(d->shadow, d->h, d->w);
+ }
+
+ wresize(d->widget, d->h, d->w);
+ mvwin(d->widget, d->y, d->x);
+ wbkgd(d->widget, t.dialog.color);
+
+ return (0);
+}
+
+int
+dialog_paint_frame(struct dialog *d)
{
int wtitle, wbottomtitle;
cchar_t ts, ltee, rtee;
if (d->conf->ascii_lines) {
setcchar(&ts, L"-", 0, 0, NULL);
- setcchar(<ee, L"+", 0, 0,NULL);
+ setcchar(<ee, L"+", 0, 0, NULL);
setcchar(&rtee, L"+", 0, 0, NULL);
} else {
ts = *WACS_HLINE;
@@ -1266,16 +1590,7 @@
rtee = *WACS_RTEE;
}
- if (d->conf->shadow) {
- wclear(d->shadow);
- wresize(d->shadow, d->h, d->w);
- mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x);
- wnoutrefresh(d->shadow);
- }
-
- wclear(d->widget);
- wresize(d->widget, d->h, d->w);
- mvwin(d->widget, d->y, d->x);
+ werase(d->widget);
draw_borders(d->conf, d->widget, RAISED);
if (d->conf->title != NULL) {
@@ -1321,9 +1636,47 @@
wattroff(d->widget, t.dialog.bottomtitlecolor);
}
+ return (0);
+}
+
+int
+dialog_repaint_frame(struct dialog *d)
+{
+
+ if (dialog_place(d) != 0)
+ return (BSDDIALOG_ERROR);
+ return (dialog_paint_frame(d));
+}
+
+int
+dialog_repaint_text(struct dialog *d, int ytext, int xtext, int upnotext,
+ int downnotext)
+{
+
+ werase(d->textpad);
+ wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS);
+ if (print_textpad(d->conf, d->textpad, d->text) != 0)
+ return (BSDDIALOG_ERROR);
+ textpad_to_widget(d, ytext, xtext, upnotext, downnotext);
+ d->built = true;
+
+ return (0);
+}
+
+int draw_dialog(struct dialog *d)
+{
+ if (dialog_repaint_frame(d) != 0)
+ return (BSDDIALOG_ERROR);
+ /*
+ * Sync the backdrop after painting, immediately before wnoutrefresh(3)
+ * on dialog windows (same order as bsddialog_gauge_draw(3)).
+ */
+ dialog_backdrop_sync();
+ if (d->conf->shadow)
+ wnoutrefresh(d->shadow);
wnoutrefresh(d->widget);
- wclear(d->textpad);
+ werase(d->textpad);
/* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */
wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS);
@@ -1357,6 +1710,8 @@
if ((d->widget = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW widget");
wbkgd(d->widget, t.dialog.color);
+ scrollok(d->widget, FALSE);
+ leaveok(d->widget, TRUE);
/* fake for textpad */
if ((d->textpad = newpad(1, 1)) == NULL)
@@ -1364,4 +1719,4 @@
wbkgd(d->textpad, t.dialog.color);
return (0);
-}
\ No newline at end of file
+}
diff --git a/contrib/bsddialog/lib/libbsddialog.c b/contrib/bsddialog/lib/libbsddialog.c
--- a/contrib/bsddialog/lib/libbsddialog.c
+++ b/contrib/bsddialog/lib/libbsddialog.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +27,7 @@
*/
#include <curses.h>
+#include <stdio.h>
#include <string.h>
#include "bsddialog.h"
@@ -51,6 +53,9 @@
error += cbreak();
error += noecho();
curs_set(0);
+ clearok(stdscr, FALSE);
+ scrollok(stdscr, FALSE);
+ leaveok(stdscr, TRUE);
if (error != OK) {
bsddialog_end();
RETURN_ERROR("Cannot init curses (keypad and cursor)");
@@ -87,12 +92,14 @@
in_bsddialog_mode = false;
return (BSDDIALOG_ERROR);
}
+ bsddialog_enter_alternate_screen();
return (BSDDIALOG_OK);
}
int bsddialog_end(void)
{
+ dialog_backtitle_clear();
if (endwin() != OK)
RETURN_ERROR("Cannot end curses (endwin)");
in_bsddialog_mode = false;
@@ -100,20 +107,46 @@
return (BSDDIALOG_OK);
}
+void
+bsddialog_keep_tite(void)
+{
+
+ bsddialog_cancel_alternate_screen();
+}
+
+void
+bsddialog_fill_backdrop(void)
+{
+
+ paint_screen_backdrop();
+ dialog_backtitle_paint();
+ /*
+ * dlg_clear(3) fills stdscr(3) in memory only. Without untouchwin(3)
+ * the next refresh(3) would flush a blue screen before the gauge is
+ * copied into stdscr(3).
+ */
+ untouchwin(stdscr);
+}
+
+void
+bsddialog_paint_screen(void)
+{
+
+ dialog_backdrop_sync();
+ dialog_flush();
+}
+
int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle)
{
CHECK_PTR(conf);
- move(0, 1);
- clrtoeol();
- addstr(CHECK_STR(backtitle));
- if (conf->no_lines != true) {
- if (conf->ascii_lines)
- mvhline(1, 1, '-', SCREENCOLS - 2);
- else
- mvhline_set(1, 1, WACS_HLINE, SCREENCOLS - 2);
- }
-
+ /*
+ * Remember text for dialog_backdrop_sync(3): the utility draws the
+ * backtitle first, then draw_dialog(3) fills stdscr(3) with the themed
+ * backdrop and would otherwise erase these lines.
+ */
+ dialog_backtitle_set(conf, CHECK_STR(backtitle));
+ dialog_backtitle_paint();
wnoutrefresh(stdscr);
return (BSDDIALOG_OK);
@@ -124,7 +157,7 @@
int rv;
rv = bsddialog_backtitle(conf, backtitle);
- doupdate();
+ dialog_flush_stdscr();
return (rv);
}
@@ -162,4 +195,4 @@
move(y, 0);
clrtobot();
refresh();
-}
\ No newline at end of file
+}
diff --git a/contrib/bsddialog/lib/menubox.c b/contrib/bsddialog/lib/menubox.c
--- a/contrib/bsddialog/lib/menubox.c
+++ b/contrib/bsddialog/lib/menubox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -392,6 +393,7 @@
addstr(pritem->bottomdesc);
attroff(t.menu.bottomdesccolor);
wnoutrefresh(stdscr);
+ dialog_flush_stdscr();
}
}
}
@@ -458,7 +460,7 @@
{
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
m->menurows = m->apimenurows;
if (menu_size_position(d, m) != 0)
@@ -466,7 +468,7 @@
if (draw_dialog(d) != 0) /* doupdate() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, 2/*bmenu*/ + m->menurows + HBUTTONS);
/* selected item in view*/
@@ -538,7 +540,7 @@
changeitem = false;
loop = true;
while (loop) {
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
switch(input) {
@@ -563,6 +565,7 @@
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_LEFT:
d.bs.curr--;
if (d.bs.curr < 0)
@@ -642,7 +645,7 @@
if (conf->menu.shortcut_buttons) {
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
if (m.pritems[m.sel].type == MENUMODE)
m.pritems[m.sel].on = true;
diff --git a/contrib/bsddialog/lib/messagebox.c b/contrib/bsddialog/lib/messagebox.c
--- a/contrib/bsddialog/lib/messagebox.c
+++ b/contrib/bsddialog/lib/messagebox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -75,14 +76,14 @@
if (redraw) { /* redraw: RESIZE or F1 */
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (message_size_position(d, &s->htext) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
s->printrows = d->h - BORDER - HBUTTONS - BORDER;
s->ypad = 0;
@@ -112,7 +113,7 @@
loop = true;
while (loop) {
textupdate(&d, &s);
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
switch (input) {
@@ -132,6 +133,7 @@
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_LEFT:
d.bs.curr--;
if (d.bs.curr < 0)
@@ -181,7 +183,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush_overlay();
retval = BUTTONVALUE(d.bs);
loop = false;
}
@@ -223,7 +225,7 @@
if (draw_dialog(&d) != 0)
return (BSDDIALOG_ERROR);
TEXTPAD(&d, 0);
- doupdate();
+ dialog_flush_overlay();
end_dialog(&d);
diff --git a/contrib/bsddialog/lib/slider.c b/contrib/bsddialog/lib/slider.c
--- a/contrib/bsddialog/lib/slider.c
+++ b/contrib/bsddialog/lib/slider.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Braulio Rivas
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -283,7 +284,9 @@
chtype ch;
getmaxyx(win, height, width);
- wclear(win);
+ /* werase(3): see print_calendar(3) in datebox.c
+ * (wclear(3) floods scrollback) */
+ werase(win);
draw_borders(conf, win, RAISED);
if (active) {
@@ -335,14 +338,14 @@
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, MINHSLIDER, MINWSLIDER, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, MINHSLIDER + HBUTTONS);
yslider = d->y + d->h - 15;
@@ -433,7 +436,7 @@
drawsquare(conf, step_win, RAISED, sel == STEP_WIN, "%15d", ctl.step);
print_slider(conf, slider_win, blocks, nblocks, length, start,
end, sel == SLIDER_WIN);
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
@@ -470,6 +473,24 @@
}
DRAW_BUTTONS(dialog);
break;
+ case KEY_BTAB: /* Shift+TAB */
+ if (focusbuttons) {
+ dialog.bs.curr--;
+ if (dialog.bs.curr < 0) {
+ focusbuttons = false;
+ sel = SLIDER_WIN;
+ dialog.bs.curr =
+ conf->button.always_active ? 0 : -1;
+ }
+ } else if (sel == START_WIN) {
+ focusbuttons = true;
+ sel = NULLWIN;
+ dialog.bs.curr = (int)dialog.bs.nbuttons - 1;
+ } else {
+ sel--;
+ }
+ DRAW_BUTTONS(dialog);
+ break;
case KEY_CTRL('n'):
case KEY_RIGHT:
if (focusbuttons) {
@@ -653,7 +674,7 @@
default:
if (shortcut_buttons(input, &dialog.bs)) {
DRAW_BUTTONS(dialog);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(dialog.bs);
loop = false;
}
diff --git a/contrib/bsddialog/lib/textbox.c b/contrib/bsddialog/lib/textbox.c
--- a/contrib/bsddialog/lib/textbox.c
+++ b/contrib/bsddialog/lib/textbox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -105,14 +106,14 @@
{
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (textbox_size_position(d, st) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* wrefresh() and prefresh() in main loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
st->ys = d->y + 1;
st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2;
@@ -185,7 +186,7 @@
* Trick, overflow multicolumn charchter right border:
* wnoutrefresh(widget);
* pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe);
- * doupdate();
+ * dialog_flush();
*/
wrefresh(d.widget);
prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe);
@@ -193,7 +194,7 @@
continue;
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush_widget(&d);
retval = BUTTONVALUE(d.bs);
break; /* loop */
}
@@ -210,7 +211,11 @@
}
break;
case '\t': /* TAB */
- d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
+ BUTTONS_TAB_FORWARD(d.bs);
+ DRAW_BUTTONS(d);
+ break;
+ case KEY_BTAB: /* Shift+TAB */
+ BUTTONS_TAB_BACKWARD(d.bs);
DRAW_BUTTONS(d);
break;
case KEY_HOME:
diff --git a/contrib/bsddialog/lib/theme.c b/contrib/bsddialog/lib/theme.c
--- a/contrib/bsddialog/lib/theme.c
+++ b/contrib/bsddialog/lib/theme.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -152,7 +153,7 @@
};
static void
-set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
+copy_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
{
dst->screen.color = src->screen.color;
@@ -200,7 +201,13 @@
dst->button.color = src->button.color;
dst->button.f_shortcutcolor = src->button.f_shortcutcolor;
dst->button.shortcutcolor = src->button.shortcutcolor;
+}
+
+static void
+set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
+{
+ copy_theme(dst, src);
bkgd(dst->screen.color);
}
@@ -208,7 +215,7 @@
int bsddialog_get_theme(struct bsddialog_theme *theme)
{
CHECK_PTR(theme);
- set_theme(theme, &t);
+ copy_theme(theme, &t);
return (BSDDIALOG_OK);
}
@@ -217,13 +224,15 @@
{
CHECK_PTR(theme);
set_theme(&t, theme);
- wnoutrefresh(stdscr);
+ dialog_flush_reset();
return (BSDDIALOG_OK);
}
-int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme)
+static int
+install_default_theme(enum bsddialog_default_theme newtheme)
{
+
if (newtheme == BSDDIALOG_THEME_3D) {
set_theme(&t, &flat);
t.dialog.lineraisecolor =
@@ -239,7 +248,27 @@
"to use enum bsddialog_default_theme",
newtheme);
}
- wnoutrefresh(stdscr);
+
+ return (BSDDIALOG_OK);
+}
+
+int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme)
+{
+
+ if (install_default_theme(newtheme) != BSDDIALOG_OK)
+ return (BSDDIALOG_ERROR);
+ dialog_flush_reset();
+
+ return (BSDDIALOG_OK);
+}
+
+int
+bsddialog_set_default_theme_noflush(enum bsddialog_default_theme newtheme)
+{
+
+ if (install_default_theme(newtheme) != BSDDIALOG_OK)
+ return (BSDDIALOG_ERROR);
+ dialog_flush_reset();
return (BSDDIALOG_OK);
}
diff --git a/contrib/bsddialog/lib/timebox.c b/contrib/bsddialog/lib/timebox.c
--- a/contrib/bsddialog/lib/timebox.c
+++ b/contrib/bsddialog/lib/timebox.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2025 Alfonso Sabato Siciliano
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -68,14 +69,14 @@
if (redraw) {
hide_dialog(d);
- refresh(); /* Important for decreasing screen */
+ dialog_flush(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0) /* doupdate() in mail loop */
return (BSDDIALOG_ERROR);
if (redraw)
- refresh(); /* Important to fix grey lines expanding screen */
+ dialog_flush(); /* Important, fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
y = d->y + d->h - BORDER - HBUTTONS - HBOX;
@@ -125,7 +126,7 @@
while (loop) {
for (i = 0; i < 3; i++)
drawsquare(conf, c[i].win, c[i].value, sel == i);
- doupdate();
+ dialog_flush_overlay();
if (get_wch(&input) == ERR)
continue;
switch(input) {
@@ -163,6 +164,7 @@
}
DRAW_BUTTONS(d);
break;
+ case KEY_BTAB: /* Shift+TAB */
case KEY_CTRL('p'):
case KEY_LEFT:
if (focusbuttons) {
@@ -199,10 +201,15 @@
break;
case '+':
case KEY_DOWN:
- if (focusbuttons)
- break;
- c[sel].value = c[sel].value < c[sel].max ?
- c[sel].value + 1 : 0;
+ if (focusbuttons) {
+ sel = 2;
+ focusbuttons = false;
+ d.bs.curr = conf->button.always_active ? 0 : -1;
+ DRAW_BUTTONS(d);
+ } else {
+ c[sel].value = c[sel].value < c[sel].max ?
+ c[sel].value + 1 : 0;
+ }
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
@@ -221,7 +228,7 @@
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
- doupdate();
+ dialog_flush();
retval = BUTTONVALUE(d.bs);
loop = false;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Aug 6, 11:02 PM (16 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36137422
Default Alt Text
D58118.id182190.diff (55 KB)
Attached To
Mode
D58118: [DNM] contrib/bsddialog: import version 1.2 rendering and API improvements
Attached
Detach File
Event Timeline
Log In to Comment