Page MenuHomeFreeBSD

D58118.id182190.diff
No OneTemporary

D58118.id182190.diff

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(&ltee, L"+", 0, 0,NULL);
+ setcchar(&ltee, 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

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)

Event Timeline