fix(tui): bind whiptail dialogs to controlling terminal
Interactive stage selection did not receive any keyboard input when the setup was started through a pipe (curl ... | bash), because the dialogs inherited the pipe as stdin/stdout and ENTER/SPACE went to the shell. - lib/utils.sh: add has_tty, ensure_term, get_term_size and a run_whiptail wrapper that binds dialog I/O to /dev/tty and returns results via --output-fd 3 - lib/tui.sh: run all dialogs through run_whiptail, add tui_is_available and tui_calc_geometry to clamp dialog geometry to the terminal size - setup.sh: re-attach stdin/stdout to /dev/tty when piped, normalize TERM and use has_tty for the interactive check - install.sh: reconnect stdout as well and normalize TERM before running setup.sh Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
+16
-3
@@ -130,11 +130,24 @@ main() {
|
|||||||
fi
|
fi
|
||||||
setup_cmd+=(./setup.sh "$@")
|
setup_cmd+=(./setup.sh "$@")
|
||||||
|
|
||||||
# Reconnect stdin to /dev/tty before executing setup.sh if piped (e.g. curl ... | bash)
|
# Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped
|
||||||
if [[ ! -t 0 ]] && [[ -e /dev/tty ]] && ( : < /dev/tty ) 2>/dev/null; then
|
# (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes
|
||||||
exec < /dev/tty
|
if [[ -c /dev/tty ]] && ( : < /dev/tty ) 2>/dev/null; then
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
exec > /dev/tty
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ensure a terminal type whiptail/newt is able to render
|
||||||
|
case "${TERM:-}" in
|
||||||
|
""|dumb|unknown)
|
||||||
|
export TERM="xterm-256color"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
"${setup_cmd[@]}"
|
"${setup_cmd[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+75
-55
@@ -5,33 +5,78 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# All whiptail dialogs are executed via 'run_whiptail' (lib/utils.sh), which
|
||||||
|
# binds stdin and stdout to the controlling terminal (/dev/tty). Without this,
|
||||||
|
# keyboard input (ENTER, SPACE, arrow keys) is consumed by the shell instead of
|
||||||
|
# the dialog whenever the setup is started through a pipe (curl ... | bash).
|
||||||
|
|
||||||
# Check if whiptail is available
|
# Check if whiptail is available
|
||||||
tui_check_whiptail() {
|
tui_check_whiptail() {
|
||||||
if ! command_exists whiptail; then
|
if ! command_exists whiptail; then
|
||||||
log_warn "whiptail is not installed. Attempting to install whiptail..."
|
log_warn "whiptail is not installed. Attempting to install whiptail..."
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail
|
||||||
fi
|
fi
|
||||||
|
ensure_term
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine whether an interactive TUI can be displayed at all
|
||||||
|
tui_is_available() {
|
||||||
|
command_exists whiptail || return 1
|
||||||
|
has_tty || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compute dialog geometry into TUI_HEIGHT / TUI_WIDTH / TUI_LIST_HEIGHT
|
||||||
|
# Usage: tui_calc_geometry <desired_height> <desired_width> [desired_list_height]
|
||||||
|
tui_calc_geometry() {
|
||||||
|
local want_height="$1"
|
||||||
|
local want_width="$2"
|
||||||
|
local want_list="${3:-0}"
|
||||||
|
|
||||||
|
local term_size term_lines term_cols
|
||||||
|
term_size="$(get_term_size)"
|
||||||
|
term_lines="${term_size%% *}"
|
||||||
|
term_cols="${term_size##* }"
|
||||||
|
|
||||||
|
local height="$want_height"
|
||||||
|
local width="$want_width"
|
||||||
|
|
||||||
|
if (( term_lines - 2 < height )); then
|
||||||
|
height=$(( term_lines - 2 ))
|
||||||
|
fi
|
||||||
|
if (( height < 8 )); then
|
||||||
|
height=8
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( term_cols - 4 < width )); then
|
||||||
|
width=$(( term_cols - 4 ))
|
||||||
|
fi
|
||||||
|
if (( width < 40 )); then
|
||||||
|
width=40
|
||||||
|
fi
|
||||||
|
|
||||||
|
local list_height="$want_list"
|
||||||
|
if (( want_list > 0 )); then
|
||||||
|
if (( height - 8 < list_height )); then
|
||||||
|
list_height=$(( height - 8 ))
|
||||||
|
fi
|
||||||
|
if (( list_height < 3 )); then
|
||||||
|
list_height=3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
TUI_HEIGHT="$height"
|
||||||
|
TUI_WIDTH="$width"
|
||||||
|
TUI_LIST_HEIGHT="$list_height"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Display welcome message
|
# Display welcome message
|
||||||
tui_welcome() {
|
tui_welcome() {
|
||||||
local term_lines
|
tui_calc_geometry 14 70
|
||||||
term_lines=$(tput lines 2>/dev/null || echo 24)
|
|
||||||
local term_cols
|
|
||||||
term_cols=$(tput cols 2>/dev/null || echo 80)
|
|
||||||
|
|
||||||
local height=14
|
|
||||||
local width=70
|
|
||||||
if (( term_lines < 16 )); then
|
|
||||||
height=$(( term_lines - 2 > 8 ? term_lines - 2 : 8 ))
|
|
||||||
fi
|
|
||||||
if (( term_cols < 72 )); then
|
|
||||||
width=$(( term_cols - 4 > 40 ? term_cols - 4 : 40 ))
|
|
||||||
fi
|
|
||||||
|
|
||||||
whiptail --title "Debian Unstable Setup & Hyprland Installer" \
|
run_whiptail --title "Debian Unstable Setup & Hyprland Installer" \
|
||||||
--msgbox "Willkommen beim Debian Unstable & Desktop Setup!\n\nDieses Skript führt dich durch die modulare Einrichtung deines Debian Sid Systems mit XanMod-Kernel, Hyprland und Optimierungen.\n\nDrücke ENTER um zur Modulauswahl zu gelangen." \
|
--msgbox "Willkommen beim Debian Unstable & Desktop Setup!\n\nDieses Skript führt dich durch die modulare Einrichtung deines Debian Sid Systems mit XanMod-Kernel, Hyprland und Optimierungen.\n\nDrücke ENTER um zur Modulauswahl zu gelangen." \
|
||||||
"$height" "$width" || true
|
"$TUI_HEIGHT" "$TUI_WIDTH" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# Present stage checklist to user
|
# Present stage checklist to user
|
||||||
@@ -40,26 +85,11 @@ tui_select_stages() {
|
|||||||
local tmp_file
|
local tmp_file
|
||||||
tmp_file="$(mktemp)"
|
tmp_file="$(mktemp)"
|
||||||
|
|
||||||
local term_lines
|
tui_calc_geometry 20 74 10
|
||||||
term_lines=$(tput lines 2>/dev/null || echo 24)
|
|
||||||
local term_cols
|
|
||||||
term_cols=$(tput cols 2>/dev/null || echo 80)
|
|
||||||
|
|
||||||
local height=20
|
|
||||||
local width=74
|
|
||||||
local list_height=10
|
|
||||||
|
|
||||||
if (( term_lines < 22 )); then
|
|
||||||
height=$(( term_lines - 2 > 12 ? term_lines - 2 : 12 ))
|
|
||||||
list_height=$(( height - 8 > 4 ? height - 8 : 4 ))
|
|
||||||
fi
|
|
||||||
if (( term_cols < 76 )); then
|
|
||||||
width=$(( term_cols - 4 > 40 ? term_cols - 4 : 40 ))
|
|
||||||
fi
|
|
||||||
|
|
||||||
if whiptail --output-fd 3 --title "Modulauswahl / Stages" \
|
if run_whiptail --output-fd 3 --title "Modulauswahl / Stages" \
|
||||||
--checklist "Wähle die gewünschten Installations-Phasen mit der LEERTASTE aus:" \
|
--checklist "Wähle die gewünschten Installations-Phasen mit der LEERTASTE aus (ENTER = OK):" \
|
||||||
"$height" "$width" "$list_height" \
|
"$TUI_HEIGHT" "$TUI_WIDTH" "$TUI_LIST_HEIGHT" \
|
||||||
"01" "Debian Unstable (Deb822, Pinning, Upgrade)" ON \
|
"01" "Debian Unstable (Deb822, Pinning, Upgrade)" ON \
|
||||||
"02" "Kernel & Hardware (XanMod, P-State, Microcode)" ON \
|
"02" "Kernel & Hardware (XanMod, P-State, Microcode)" ON \
|
||||||
"03" "Bootloader & Theme (GRUB, Vimix, Plymouth)" ON \
|
"03" "Bootloader & Theme (GRUB, Vimix, Plymouth)" ON \
|
||||||
@@ -103,11 +133,12 @@ tui_prompt_target_user() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$default_user" && "$default_user" != "root" ]]; then
|
if [[ -n "$default_user" && "$default_user" != "root" ]]; then
|
||||||
if whiptail --title "Ziel-Benutzer bestätigen" \
|
tui_calc_geometry 10 65
|
||||||
|
if run_whiptail --title "Ziel-Benutzer bestätigen" \
|
||||||
--yes-button "Verwenden" \
|
--yes-button "Verwenden" \
|
||||||
--no-button "Anderer Benutzer" \
|
--no-button "Anderer Benutzer" \
|
||||||
--yesno "Erkannter Ziel-Benutzer: $default_user\n\nMöchtest du diesen Benutzer für die Konfiguration (Dotfiles, Gruppen, Hyprland) verwenden?" \
|
--yesno "Erkannter Ziel-Benutzer: $default_user\n\nMöchtest du diesen Benutzer für die Konfiguration (Dotfiles, Gruppen, Hyprland) verwenden?" \
|
||||||
10 65; then
|
"$TUI_HEIGHT" "$TUI_WIDTH"; then
|
||||||
chosen="$default_user"
|
chosen="$default_user"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -116,19 +147,20 @@ tui_prompt_target_user() {
|
|||||||
tmp_file="$(mktemp)"
|
tmp_file="$(mktemp)"
|
||||||
|
|
||||||
while [[ -z "$chosen" ]]; do
|
while [[ -z "$chosen" ]]; do
|
||||||
if whiptail --output-fd 3 --title "Ziel-Benutzer eingeben" \
|
tui_calc_geometry 10 60
|
||||||
|
if run_whiptail --output-fd 3 --title "Ziel-Benutzer eingeben" \
|
||||||
--inputbox "Bitte gib den Benutzernamen des Desktop-Benutzers ein:" \
|
--inputbox "Bitte gib den Benutzernamen des Desktop-Benutzers ein:" \
|
||||||
10 60 "$default_user" \
|
"$TUI_HEIGHT" "$TUI_WIDTH" "$default_user" \
|
||||||
3> "$tmp_file"; then
|
3> "$tmp_file"; then
|
||||||
|
|
||||||
local input_user
|
local input_user
|
||||||
input_user="$(tr -d '[:space:]' < "$tmp_file")"
|
input_user="$(tr -d '[:space:]' < "$tmp_file")"
|
||||||
if [[ -z "$input_user" || "$input_user" == "root" ]]; then
|
if [[ -z "$input_user" || "$input_user" == "root" ]]; then
|
||||||
whiptail --title "Ungültiger Benutzer" --msgbox "Ungültiger Benutzername (darf nicht leer oder root sein)." 8 55
|
run_whiptail --title "Ungültiger Benutzer" --msgbox "Ungültiger Benutzername (darf nicht leer oder root sein)." 8 55 || true
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if ! id "$input_user" >/dev/null 2>&1; then
|
if ! id "$input_user" >/dev/null 2>&1; then
|
||||||
whiptail --title "Benutzer nicht gefunden" --msgbox "Benutzer '$input_user' existiert nicht im System." 8 55
|
run_whiptail --title "Benutzer nicht gefunden" --msgbox "Benutzer '$input_user' existiert nicht im System." 8 55 || true
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
chosen="$input_user"
|
chosen="$input_user"
|
||||||
@@ -148,21 +180,9 @@ tui_confirm_execution() {
|
|||||||
local selected_stages="$1"
|
local selected_stages="$1"
|
||||||
local target_user="$2"
|
local target_user="$2"
|
||||||
|
|
||||||
local term_lines
|
tui_calc_geometry 14 70
|
||||||
term_lines=$(tput lines 2>/dev/null || echo 24)
|
|
||||||
local term_cols
|
|
||||||
term_cols=$(tput cols 2>/dev/null || echo 80)
|
|
||||||
|
|
||||||
local height=14
|
|
||||||
local width=70
|
|
||||||
if (( term_lines < 16 )); then
|
|
||||||
height=$(( term_lines - 2 > 10 ? term_lines - 2 : 10 ))
|
|
||||||
fi
|
|
||||||
if (( term_cols < 72 )); then
|
|
||||||
width=$(( term_cols - 4 > 40 ? term_cols - 4 : 40 ))
|
|
||||||
fi
|
|
||||||
|
|
||||||
whiptail --title "Installation starten" \
|
run_whiptail --title "Installation starten" \
|
||||||
--yesno "Folgende Konfiguration wird ausgeführt:\n\n- Ziel-Benutzer: $target_user\n- Ausgewählte Stages: $selected_stages\n\nMöchtest du die Installation jetzt starten?" \
|
--yesno "Folgende Konfiguration wird ausgeführt:\n\n- Ziel-Benutzer: $target_user\n- Ausgewählte Stages: $selected_stages\n\nMöchtest du die Installation jetzt starten?" \
|
||||||
"$height" "$width"
|
"$TUI_HEIGHT" "$TUI_WIDTH"
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-5
@@ -82,6 +82,59 @@ command_exists() {
|
|||||||
command -v "$1" >/dev/null 2>&1
|
command -v "$1" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Terminal / TTY helpers (required for interactive whiptail dialogs)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Check whether a usable controlling terminal is available
|
||||||
|
has_tty() {
|
||||||
|
[[ -c /dev/tty ]] || return 1
|
||||||
|
{ : < /dev/tty; } 2>/dev/null || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure TERM is set to a value newt/whiptail is able to render
|
||||||
|
ensure_term() {
|
||||||
|
case "${TERM:-}" in
|
||||||
|
""|dumb|unknown)
|
||||||
|
if [[ -f /usr/share/terminfo/x/xterm-256color || -f /lib/terminfo/x/xterm-256color ]]; then
|
||||||
|
export TERM="xterm-256color"
|
||||||
|
else
|
||||||
|
export TERM="linux"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Report the current terminal size as "<lines> <columns>"
|
||||||
|
get_term_size() {
|
||||||
|
local size=""
|
||||||
|
if has_tty; then
|
||||||
|
size="$(stty size < /dev/tty 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$size" ]]; then
|
||||||
|
size="$(stty size 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [[ ! "$size" =~ ^[0-9]+[[:space:]]+[0-9]+$ ]]; then
|
||||||
|
size="${LINES:-24} ${COLUMNS:-80}"
|
||||||
|
fi
|
||||||
|
echo "$size"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run whiptail with stdin and stdout bound to the controlling terminal.
|
||||||
|
# This is required because the setup may be started through a pipe
|
||||||
|
# (e.g. 'curl ... | bash'), where stdin is not the terminal and all
|
||||||
|
# keystrokes would otherwise be swallowed by the shell instead of the dialog.
|
||||||
|
# Dialog results must be requested via '--output-fd 3'.
|
||||||
|
run_whiptail() {
|
||||||
|
ensure_term
|
||||||
|
if has_tty; then
|
||||||
|
whiptail "$@" < /dev/tty > /dev/tty
|
||||||
|
else
|
||||||
|
whiptail "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Determine and prompt for the non-root target user
|
# Determine and prompt for the non-root target user
|
||||||
prompt_target_user() {
|
prompt_target_user() {
|
||||||
if [[ -n "${TARGET_USER:-}" ]]; then
|
if [[ -n "${TARGET_USER:-}" ]]; then
|
||||||
@@ -203,19 +256,22 @@ prompt_ollama_models_path() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# If whiptail is available and running on an active terminal, use whiptail dialog
|
# If whiptail is available and running on an active terminal, use whiptail dialog
|
||||||
if command_exists whiptail && [[ -t 1 || -c /dev/tty ]]; then
|
if command_exists whiptail && has_tty; then
|
||||||
local tui_input
|
local tui_tmp
|
||||||
if tui_input=$(whiptail --title "Ollama KI-Modelle Speicherort" \
|
tui_tmp="$(mktemp)"
|
||||||
|
if run_whiptail --output-fd 3 --title "Ollama KI-Modelle Speicherort" \
|
||||||
--inputbox "Bitte gib den Speicherort für die Ollama KI-Modelle an:" \
|
--inputbox "Bitte gib den Speicherort für die Ollama KI-Modelle an:" \
|
||||||
10 70 "$default_path" \
|
10 70 "$default_path" \
|
||||||
3>&1 1>&2 2>&3); then
|
3> "$tui_tmp"; then
|
||||||
tui_input="$(echo "$tui_input" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
local tui_input
|
||||||
|
tui_input="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' < "$tui_tmp")"
|
||||||
if [[ -n "$tui_input" ]]; then
|
if [[ -n "$tui_input" ]]; then
|
||||||
chosen="$tui_input"
|
chosen="$tui_input"
|
||||||
else
|
else
|
||||||
chosen="$default_path"
|
chosen="$default_path"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
rm -f "$tui_tmp"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# CLI fallback if whiptail was not used or failed
|
# CLI fallback if whiptail was not used or failed
|
||||||
|
|||||||
@@ -21,6 +21,19 @@ fi
|
|||||||
|
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
|
|
||||||
|
# Reconnect stdin/stdout to the controlling terminal when the script was
|
||||||
|
# started through a pipe (e.g. 'curl ... | bash'). Otherwise keystrokes are
|
||||||
|
# consumed by the shell and never reach the interactive whiptail dialogs.
|
||||||
|
if has_tty; then
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
exec > /dev/tty
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
ensure_term
|
||||||
|
|
||||||
# Show help text
|
# Show help text
|
||||||
show_help() {
|
show_help() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
@@ -130,7 +143,7 @@ elif [[ -n "$FLAG_STAGES" ]]; then
|
|||||||
done
|
done
|
||||||
else
|
else
|
||||||
# Interactive TUI Mode
|
# Interactive TUI Mode
|
||||||
if [[ -t 0 && -t 1 ]]; then
|
if has_tty; then
|
||||||
tui_check_whiptail
|
tui_check_whiptail
|
||||||
tui_welcome
|
tui_welcome
|
||||||
if ! tui_select_stages; then
|
if ! tui_select_stages; then
|
||||||
@@ -156,6 +169,7 @@ else
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
log_warn "No controlling terminal available - interactive TUI cannot be displayed."
|
||||||
log_info "Non-interactive shell and no parameters provided. Defaulting to --all."
|
log_info "Non-interactive shell and no parameters provided. Defaulting to --all."
|
||||||
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10)
|
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10)
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user