Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rdi-installer for openSUSE:Factory checked in at 2026-10-14 18:35:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rdi-installer (Old) and /work/SRC/openSUSE:Factory/.rdi-installer.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rdi-installer" Wed Oct 14 18:35:07 2026 rev:3 rq:1384656 version:1.2.0+git20261013.6276d8c Changes: -------- --- /work/SRC/openSUSE:Factory/rdi-installer/rdi-installer.changes 2026-10-08 11:58:52.959716397 +0200 +++ /work/SRC/openSUSE:Factory/.rdi-installer.new.3698736/rdi-installer.changes 2026-10-14 18:35:50.550689014 +0200 @@ -1,0 +2,10 @@ +Tue Oct 13 15:50:48 UTC 2026 - Thorsten Kukuk <[email protected]> + +- Update to version 1.2.0+git20261013.6276d8c: + * Release version 1.2.0 + * Remove outdated todo list + * Add "Settings" menu in order to set keymap, image download server and ssh key handling. (#25) + * exec_cmd: fix waitpid error handling + * Remove home:kukuk:mkosi-images key from development + +------------------------------------------------------------------- Old: ---- rdi-installer-1.1.0+git20261007.d929392.tar.xz New: ---- rdi-installer-1.2.0+git20261013.6276d8c.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rdi-installer.spec ++++++ --- /var/tmp/diff_new_pack.dNAr7J/_old 2026-10-14 18:35:52.440768593 +0200 +++ /var/tmp/diff_new_pack.dNAr7J/_new 2026-10-14 18:35:52.442768677 +0200 @@ -16,7 +16,7 @@ # Name: rdi-installer -Version: 1.1.0+git20261007.d929392 +Version: 1.2.0+git20261013.6276d8c Release: 0 Summary: Utility to write disk images to hard disk License: MIT ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.dNAr7J/_old 2026-10-14 18:35:52.489770656 +0200 +++ /var/tmp/diff_new_pack.dNAr7J/_new 2026-10-14 18:35:52.492770782 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/thkukuk/rdi-installer.git</param> -<param name="changesrevision">d929392abddf784c686ede2114e11b0997102138</param></service></servicedata> +<param name="changesrevision">6276d8c9cc21dbe78a3ab99556eaf0a0e4204705</param></service></servicedata> (No newline at EOF) ++++++ rdi-installer-1.1.0+git20261007.d929392.tar.xz -> rdi-installer-1.2.0+git20261013.6276d8c.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/TODO new/rdi-installer-1.2.0+git20261013.6276d8c/TODO --- old/rdi-installer-1.1.0+git20261007.d929392/TODO 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/TODO 1970-01-01 01:00:00.000000000 +0100 @@ -1,60 +0,0 @@ -Goal: - -Create a raw disk image installer, which dumps an image to a disk. -With this setup, we have two stages during installation: - -1. Dump a prebuild disk image to harddisk - - This does not contain any modifications of the content of the image! -2. Personalization of the OS during first boot - - Resize image - - Create additional partitions - - Create users, set passwords, install ssh keys - - Configure and enable services - ... - -For the second part we have ignition, combustion and cloud-init, depending on the kind of image. - -For the first part we need something new, this raw-disk-image-installer: -- Small, simple, fast -- Low memory usage (needs to work with 1GB RAM, preferable 512 MB) => not possible since we run out of memory (PXE, UEFI HTTP(S)) - - Edge Systems - - Big embedded devices -- Robust - - Always check for errors everywhere including error handling - - Always print errors or save in log files accessible to the user -- User-friendly - - Pre-defined/pre-filled values that can be customized -- Secure - - No remote ssh login with default user/password? - - Strict certificate checks e.g. for https? - - compare hashes and gpg signatures - - ... -- Single disk setup (root and data on same disk) -- Multi disk setup - - Special case, root and data on separate disks. This is "Single disk setup" where the data will be generated at first boot, e.g. systemd-repart and combustion -- Remote disks: - - iSCSI - - FCoE -- MD RAID setups - - RAID 1 for the OS - - RAID 5 for data or both on RAID 5) -- Multipath -- Network setup without DHCP available (similar to linuxrc netsetup=XXX) - - Provide static IP - - Propose network setup for installation, let admin adjust it -- Automatable/configurable -- Boot installer via: - - ISO - - USB stick - - PXE/iPXE/zPXE (s390x) - - UEFI HTTP(S) Boot -- Local or remote image source (USB, ISO, network via https, http, ftp, tftp, local file) - - Air gapped - - Proxy access - - IPv4 and IPv6 -- Support compressed and uncompressed images -- Deploy config for ignition/combustion/cloud-init - - done via "add_extra_partition script" - -TODO: -- Make sure rdii-networkd understands all dracut-style options (**ip=**, etc.) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/include/keymap.h new/rdi-installer-1.2.0+git20261013.6276d8c/include/keymap.h --- old/rdi-installer-1.1.0+git20261007.d929392/include/keymap.h 1970-01-01 01:00:00.000000000 +0100 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/include/keymap.h 2026-10-13 17:49:46.000000000 +0200 @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// Dynamic list of available system keymaps, populated by load_system_keymaps() +extern char **all_keymaps; +extern int total_keymaps; + +extern int set_keymap(const char *keymap); +extern int get_vconsole_keymap(char **ret); +extern int load_system_keymaps(void); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/include/select_keymap.h new/rdi-installer-1.2.0+git20261013.6276d8c/include/select_keymap.h --- old/rdi-installer-1.1.0+git20261007.d929392/include/select_keymap.h 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/include/select_keymap.h 2026-10-13 17:49:46.000000000 +0200 @@ -2,5 +2,6 @@ #pragma once -extern int set_keymap(const char *keymap); +#include "keymap.h" + extern int select_keymap(char **device); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/lib/exec_cmd.c new/rdi-installer-1.2.0+git20261013.6276d8c/lib/exec_cmd.c --- old/rdi-installer-1.1.0+git20261007.d929392/lib/exec_cmd.c 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/lib/exec_cmd.c 2026-10-13 17:49:46.000000000 +0200 @@ -58,11 +58,13 @@ // Wait for the child process to complete int wait_status; - if (waitpid(pid, &wait_status, 0) == -1) + while (waitpid(pid, &wait_status, 0) == -1) { + if (errno == EINTR) + continue; if (use_ncurses) reset_prog_mode(); - return -r; + return -errno; } if (use_ncurses) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/lib/keymap.c new/rdi-installer-1.2.0+git20261013.6276d8c/lib/keymap.c --- old/rdi-installer-1.1.0+git20261007.d929392/lib/keymap.c 1970-01-01 01:00:00.000000000 +0100 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/lib/keymap.c 2026-10-13 17:49:46.000000000 +0200 @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "config.h" + +#include <ftw.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <libeconf.h> + +#include "basics.h" +#include "exec_cmd.h" +#include "nc-dialogs.h" +#include "keymap.h" +#include "logger.h" + +int +set_keymap(const char *keymap) +{ + int r; + + print_global_header_footer(NULL, NO_SELECTION); + move(2,2); + refresh(); + + MSG_INFO("Set keymap to %s", keymap); + r = exec_cmd(true, "loadkeys", "loadkeys", keymap, NULL); + if (r != 0) + show_error_popup("Cannot set keymap with the call 'loadkeys' to:", keymap, NULL); + return r; +} + +int +get_vconsole_keymap(char **ret) +{ + _cleanup_(econf_freeFilep) econf_file *key_file = NULL; + _cleanup_free_ char *keymap = NULL; + econf_err error; + + error = econf_readFile(&key_file, "/etc/vconsole.conf", "=", "#"); + if (error != ECONF_SUCCESS) + { + show_error_popup("Cannot read console keymap", + "Failed to read /etc/vconsole.conf:", econf_errString(error)); + return -error; + } + + error = econf_getStringValue(key_file, NULL, "KEYMAP", &keymap); + if (error != ECONF_SUCCESS) + { + if (error == ECONF_NOKEY) + return 0; + else + return -error; + } + + *ret = TAKE_PTR(keymap); + return 0; +} + +// Dynamic list of available keymaps +char **all_keymaps = NULL; +int total_keymaps = 0; +static int capacity_keymaps = 0; + +static int +compare_strings(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +static int +process_file(const char *fpath, const struct stat *sb _unused_, + int typeflag, struct FTW *ftwbuf) +{ + if (typeflag != FTW_F) // If it's not a regular file + return 0; + + const char *filename = fpath + ftwbuf->base; + + // Look for .map or .kmap extensions (ignoring the .gz part) + if (strstr(filename, ".map") == NULL && + strstr(filename, ".kmap") == NULL) + return 0; + + if (total_keymaps >= capacity_keymaps) + { + int new_capacity = capacity_keymaps == 0 ? 128 : capacity_keymaps * 2; + char **tmp = realloc(all_keymaps, new_capacity * sizeof(char *)); + if (!tmp) + { + MSG_ERROR("Out of memory!"); + return -ENOMEM; + } + all_keymaps = tmp; + capacity_keymaps = new_capacity; + } + + // Duplicate and clean the filename + _cleanup_free_ char *clean_name = strdup(filename); + if (!clean_name) + { + MSG_ERROR("Out of memory!"); + return -ENOMEM; + } + + // Strip the extension (.map or .kmap and anything after) + char *dot = strstr(clean_name, ".kmap"); + if (!dot) + dot = strstr(clean_name, ".map"); + if (dot) + *dot = '\0'; + + // Prevent exact duplicates in the list + int is_duplicate = 0; + for (int i = 0; i < total_keymaps; i++) + { + if (streq(all_keymaps[i], clean_name)) + { + is_duplicate = 1; + break; + } + } + + if (!is_duplicate) + all_keymaps[total_keymaps++] = TAKE_PTR(clean_name); + + return 0; +} + +int +load_system_keymaps(void) +{ + int r; + + r = nftw("/usr/share/kbd/keymaps", process_file, 20, FTW_PHYS); + if (r < 0) + { + show_error_popup("Cannot read available keymapts", "nftw('/usr/share/kbd/keymaps') failed", NULL); + return -1; + } + + if (total_keymaps > 1) + qsort(all_keymaps, total_keymaps, sizeof(char *), compare_strings); + + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/lib/select_keymap.c new/rdi-installer-1.2.0+git20261013.6276d8c/lib/select_keymap.c --- old/rdi-installer-1.1.0+git20261007.d929392/lib/select_keymap.c 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/lib/select_keymap.c 2026-10-13 17:49:46.000000000 +0200 @@ -2,69 +2,17 @@ #include "config.h" -#include <ftw.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <ctype.h> -#include <unistd.h> -#include <libeconf.h> #include "basics.h" -#include "exec_cmd.h" #include "nc-dialogs.h" +#include "keymap.h" #include "select_keymap.h" #include "logger.h" -int -set_keymap(const char *keymap) -{ - int r; - - print_global_header_footer(NULL, NO_SELECTION); - move(2,2); - refresh(); - - MSG_INFO("Set keymap to %s", keymap); - r = exec_cmd(true, "loadkeys", "loadkeys", keymap, NULL); - if (r != 0) - show_error_popup("Cannot set keymap with the call 'loadkeys' to:", keymap, NULL); - return r; -} - -static int -get_vconsole_keymap(char **ret) -{ - _cleanup_(econf_freeFilep) econf_file *key_file = NULL; - _cleanup_free_ char *keymap = NULL; - econf_err error; - - error = econf_readFile(&key_file, "/etc/vconsole.conf", "=", "#"); - if (error != ECONF_SUCCESS) - { - show_error_popup("Cannot read console keymap", - "Failed to read /etc/vconsole.conf:", econf_errString(error)); - return -error; - } - - error = econf_getStringValue(key_file, NULL, "KEYMAP", &keymap); - if (error != ECONF_SUCCESS) - { - if (error == ECONF_NOKEY) - return 0; - else - return -error; - } - - *ret = TAKE_PTR(keymap); - return 0; -} - -// Dynamic list of available keymaps -char **all_keymaps = NULL; -int total_keymaps = 0; -int capacity_keymaps = 0; - #define MAX_FILTER_LEN 50 const char **filtered_keymaps = NULL; int filtered_count = 0; @@ -72,89 +20,6 @@ int list_offset = 0; char filter_buf[MAX_FILTER_LEN] = ""; -static int -compare_strings(const void *a, const void *b) -{ - return strcmp(*(const char **)a, *(const char **)b); -} - -static int -process_file(const char *fpath, const struct stat *sb _unused_, - int typeflag, struct FTW *ftwbuf) -{ - if (typeflag != FTW_F) // If it's not a regular file - return 0; - - const char *filename = fpath + ftwbuf->base; - - // Look for .map or .kmap extensions (ignoring the .gz part) - if (strstr(filename, ".map") == NULL && - strstr(filename, ".kmap") == NULL) - return 0; - - if (total_keymaps >= capacity_keymaps) - { - int new_capacity = capacity_keymaps == 0 ? 128 : capacity_keymaps * 2; - char **tmp = realloc(all_keymaps, new_capacity * sizeof(char *)); - if (!tmp) - { - MSG_ERROR("Out of memory!"); - return -ENOMEM; - } - all_keymaps = tmp; - capacity_keymaps = new_capacity; - } - - // Duplicate and clean the filename - _cleanup_free_ char *clean_name = strdup(filename); - if (!clean_name) - { - MSG_ERROR("Out of memory!"); - return -ENOMEM; - } - - // Strip the extension (.map or .kmap and anything after) - char *dot = strstr(clean_name, ".kmap"); - if (!dot) - dot = strstr(clean_name, ".map"); - if (dot) - *dot = '\0'; - - // Prevent exact duplicates in the list - int is_duplicate = 0; - for (int i = 0; i < total_keymaps; i++) - { - if (streq(all_keymaps[i], clean_name)) - { - is_duplicate = 1; - break; - } - } - - if (!is_duplicate) - all_keymaps[total_keymaps++] = TAKE_PTR(clean_name); - - return 0; -} - -static int -load_system_keymaps(void) -{ - int r; - - r = nftw("/usr/share/kbd/keymaps", process_file, 20, FTW_PHYS); - if (r < 0) - { - show_error_popup("Cannot read available keymapts", "nftw('/usr/share/kbd/keymaps') failed", NULL); - return -1; - } - - if (total_keymaps > 1) - qsort(all_keymaps, total_keymaps, sizeof(char *), compare_strings); - - return 0; -} - static void update_filter(void) { @@ -185,38 +50,18 @@ attroff(COLOR_PAIR(CP_UNSELECTED) | A_UNDERLINE); int list_start_y = 6; - int visible_lines = LINES - list_start_y - 2; + int visible_lines = list_viewport_height(list_start_y); - // Adjust scrolling offset - if (selected_index >= list_offset + visible_lines) - list_offset = selected_index - visible_lines + 1; - else if (selected_index < list_offset && selected_index != -1) - list_offset = selected_index; + if (selected_index != -1) + list_offset = scroll_offset_for_selection(selected_index, list_offset, visible_lines); if (total_keymaps == 0) mvprintw(list_start_y, 2, "(Error: Could not find system keymaps)"); else if (filtered_count == 0) mvprintw(list_start_y, 2, "(No keymaps match filter)"); else - { - for (int i = 0; i < visible_lines && (list_offset + i) < filtered_count; i++) - { - int current_item = list_offset + i; - - if (current_item == selected_index) - { - attron(COLOR_PAIR(CP_SELECTED)); - mvprintw(list_start_y + i, 2, "-> %s", filtered_keymaps[current_item]); - attroff(COLOR_PAIR(CP_SELECTED)); - } - else - { - attron(COLOR_PAIR(CP_UNSELECTED)); - mvprintw(list_start_y + i, 2, " %s", filtered_keymaps[current_item]); - attroff(COLOR_PAIR(CP_UNSELECTED)); - } - } - } + render_scrollable_list(list_start_y, visible_lines, filtered_keymaps, + filtered_count, selected_index, list_offset); move(4, 10 + strlen(filter_buf)); curs_set(1); refresh(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/meson.build new/rdi-installer-1.2.0+git20261013.6276d8c/meson.build --- old/rdi-installer-1.1.0+git20261007.d929392/meson.build 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/meson.build 2026-10-13 17:49:46.000000000 +0200 @@ -14,7 +14,7 @@ 'werror=true', 'warning_level=2'], license : ['MIT', 'GPL-2.0-or-later'], - version : '1.1.0', + version : '1.2.0', ) conf = configuration_data() @@ -84,7 +84,7 @@ librdii_c = files('lib/exec_cmd.c', 'lib/mkdir_p.c', 'lib/tmpfile-util.c', 'lib/logger.c', 'lib/zap_partition_table.c', 'lib/rm_rf.c', 'lib/download.c', 'lib/nc-dialogs.c', - 'lib/select_keymap.c', 'lib/is_linux_vt.c') + 'lib/keymap.c', 'lib/select_keymap.c', 'lib/is_linux_vt.c') librdii = static_library( 'rdii', librdii_c, @@ -125,7 +125,8 @@ 'src/rdii-menu-disk.c', 'src/rdii-menu-image.c', 'src/rdii-select-online-image.c', 'src/rdii-menu-sysinfo.c', 'src/rdii-menu-installation.c', - 'src/rdii-menu-mdraid.c', 'src/rdii-autoinstall.c'] + 'src/rdii-menu-mdraid.c', 'src/rdii-menu-settings.c', + 'src/rdii-autoinstall.c'] executable('rdi-installer', rdi_installer_c, include_directories : inc, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu-settings.c new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu-settings.c --- old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu-settings.c 1970-01-01 01:00:00.000000000 +0100 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu-settings.c 2026-10-13 17:49:46.000000000 +0200 @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "config.h" + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "basics.h" +#include "nc-dialogs.h" +#include "rdii-menu.h" +#include "logger.h" +#include "keymap.h" +#include "is_linux_vt.h" + +#define FILTER_BUF_LEN 50 +#define URL_BUF_LEN 256 + +// Top-to-bottom field order of the Settings screen +enum focus +{ + FOCUS_CHECKBOX, // "Copy preserved host ssh keys into installed system." checkbox + FOCUS_DOWNLOAD_SERVER, // download server URL field + FOCUS_LIST, // keymap filter + list (selected_index is valid) +}; + +static void +update_filter(const char *filter_buf, const char **filtered_keymaps, + int *filtered_count, int *selected_index, int *list_offset) +{ + *filtered_count = 0; + for (int i = 0; i < total_keymaps; i++) + { + if (filter_buf[0] == '\0' || strstr(all_keymaps[i], filter_buf)) + filtered_keymaps[(*filtered_count)++] = all_keymaps[i]; + } + + if (*selected_index >= *filtered_count) + *selected_index = *filtered_count > 0 ? 0 : -1; + + *list_offset = 0; // Reset scroll on new filter input +} + +static void +draw_settings(const char *filter_buf, const char **filtered_keymaps, + int filtered_count, enum focus focus, int selected_index, + int *list_offset, bool preserve_ssh_hostkey, const char *url_buf) +{ + print_global_header_footer(NULL, NO_SELECTION); + print_title("Settings"); + + // Custom footer: "Up/Down: Navigate" only makes sense while the keymap + // list has focus, so only show it there. + { + const char *footer_text = (focus == FOCUS_LIST) ? + "Tab: Switch Field | Up/Down: Navigate | Enter: Select | ESC: Quit" : + "Tab: Switch Field | Enter: Select | ESC: Quit"; + + attron(COLOR_PAIR(CP_FOOTER) | A_REVERSE); + mvhline(LINES - 1, 0, ' ', COLS); + mvprintw(LINES - 1, 1, "%s", footer_text); + attroff(COLOR_PAIR(CP_FOOTER) | A_REVERSE); + } + + int checkbox_row = 4; + bool checkbox_focused = (focus == FOCUS_CHECKBOX); + attron(COLOR_PAIR(checkbox_focused ? CP_SELECTED : CP_UNSELECTED)); + mvprintw(checkbox_row, 2, "[%s] Copy preserved host ssh keys into installed system.", preserve_ssh_hostkey ? "x" : " "); + attroff(COLOR_PAIR(checkbox_focused ? CP_SELECTED : CP_UNSELECTED)); + + attron(COLOR_PAIR(CP_UNSELECTED)); + mvhline(checkbox_row + 1, 2, ACS_HLINE, COLS - 4); + attroff(COLOR_PAIR(CP_UNSELECTED)); + + int download_row = checkbox_row + 2; // separator line drawn above + bool download_focused = (focus == FOCUS_DOWNLOAD_SERVER); + attron(COLOR_PAIR(download_focused ? CP_SELECTED : CP_UNSELECTED)); + mvprintw(download_row, 2, "Image Download Server: "); + attroff(COLOR_PAIR(download_focused ? CP_SELECTED : CP_UNSELECTED)); + attron(COLOR_PAIR(download_focused ? CP_SELECTED : CP_UNSELECTED) | A_UNDERLINE); + printw("%s", url_buf); + attroff(COLOR_PAIR(download_focused ? CP_SELECTED : CP_UNSELECTED) | A_UNDERLINE); + + attron(COLOR_PAIR(CP_UNSELECTED)); + mvhline(download_row + 1, 2, ACS_HLINE, COLS - 4); + attroff(COLOR_PAIR(CP_UNSELECTED)); + + int filter_row = download_row + 2; // separator line drawn above + bool filter_focused = (focus == FOCUS_LIST); + attron(COLOR_PAIR(filter_focused ? CP_SELECTED : CP_UNSELECTED)); + mvprintw(filter_row, 2, "Keymap Filter: "); + attroff(COLOR_PAIR(filter_focused ? CP_SELECTED : CP_UNSELECTED)); + attron(COLOR_PAIR(filter_focused ? CP_SELECTED : CP_UNSELECTED) | A_UNDERLINE); + printw("%s", filter_buf); + attroff(COLOR_PAIR(filter_focused ? CP_SELECTED : CP_UNSELECTED) | A_UNDERLINE); + + int list_start_y = filter_row + 2; // +1 blank separator line + int visible_lines = list_viewport_height(list_start_y); + + int list_selected = (focus == FOCUS_LIST) ? selected_index : -1; + if (focus == FOCUS_LIST) + *list_offset = scroll_offset_for_selection(selected_index, *list_offset, visible_lines); + + if (total_keymaps == 0) + mvprintw(list_start_y, 2, "(Error: Could not find system keymaps)"); + else if (filtered_count == 0) + mvprintw(list_start_y, 2, "(No keymaps match filter)"); + else + render_scrollable_list(list_start_y, visible_lines, filtered_keymaps, + filtered_count, list_selected, *list_offset); + + switch (focus) + { + case FOCUS_CHECKBOX: + curs_set(0); + break; + case FOCUS_DOWNLOAD_SERVER: + move(download_row, 25 + strlen(url_buf)); + curs_set(1); + break; + case FOCUS_LIST: + default: + move(filter_row, 17 + strlen(filter_buf)); + curs_set(1); + break; + } + + refresh(); +} + +int +settings(char **keymap, bool *preserve_ssh_hostkey) +{ + _cleanup_free_ const char **filtered_keymaps = NULL; + char filter_buf[FILTER_BUF_LEN] = ""; + char url_buf[URL_BUF_LEN] = ""; + int filtered_count = 0; + int selected_index = 0; + int saved_list_index = 0; // remembers the list position while another field has focus + int list_offset = 0; + enum focus focus = FOCUS_CHECKBOX; + int ch; + int r; + + MSG_FUNC("keymap='%s', preserve_ssh_hostkey=%i", + strempty(*keymap), *preserve_ssh_hostkey); + + if (!all_keymaps) + { + r = load_system_keymaps(); + if (r < 0) + return -1; + } + + filtered_keymaps = malloc((total_keymaps == 0 ? 1 : total_keymaps) + * sizeof(const char *)); + if (!filtered_keymaps) + return -ENOMEM; + + { + _cleanup_free_ char *system_keymap = NULL; + const char *filter_seed = *keymap; + + if (isempty(filter_seed)) + { + get_vconsole_keymap(&system_keymap); + filter_seed = system_keymap; + } + + if (!isempty(filter_seed) && strlen(filter_seed) < FILTER_BUF_LEN) + strcpy(filter_buf, filter_seed); + } + update_filter(filter_buf, filtered_keymaps, &filtered_count, &selected_index, &list_offset); + + if (!isempty(rdii_download_server) && strlen(rdii_download_server) < URL_BUF_LEN) + strcpy(url_buf, rdii_download_server); + + while (1) + { + draw_settings(filter_buf, filtered_keymaps, filtered_count, focus, + selected_index, &list_offset, *preserve_ssh_hostkey, url_buf); + ch = getch(); + curs_set(0); + + if (ch == 27) // ESC key: leave the Settings screen + break; + else if (ch == '\n' || ch == KEY_ENTER) + { + if (focus == FOCUS_CHECKBOX) + *preserve_ssh_hostkey = !*preserve_ssh_hostkey; + else if (focus == FOCUS_DOWNLOAD_SERVER) + { + const char *error_msg = NULL; + + if (isempty(url_buf)) + continue; + + if (!url_is_valid(url_buf, &error_msg) && + !show_warning_popup("URL doesn't seem to be valid:", + error_msg, "Really use this download server?")) + continue; + + char *new_server = strdup(url_buf); + if (!new_server) + return -ENOMEM; + rdii_download_server = new_server; // intentionally not freeing the previous value + } + else if (focus == FOCUS_LIST && filtered_count > 0) + { + if (is_linux_vt() || + show_warning_popup("Keymaps can only be configured directly on a virtual console.", + NULL, "Continue?")) + { + if (set_keymap(filtered_keymaps[selected_index]) == 0) + { + *keymap = mfree(*keymap); + *keymap = strdup(filtered_keymaps[selected_index]); + if (!*keymap) + return -ENOMEM; + } + } + } + } + else if (ch == KEY_UP) + { + if (focus == FOCUS_LIST) + { + if (selected_index > 0) + selected_index--; + else + { + saved_list_index = selected_index; + focus = FOCUS_DOWNLOAD_SERVER; + } + } + else if (focus == FOCUS_DOWNLOAD_SERVER) + focus = FOCUS_CHECKBOX; + // FOCUS_CHECKBOX is the top-most field, nothing above it + } + else if (ch == KEY_DOWN) + { + if (focus == FOCUS_CHECKBOX) + focus = FOCUS_DOWNLOAD_SERVER; + else if (focus == FOCUS_DOWNLOAD_SERVER) + { + focus = FOCUS_LIST; + selected_index = (filtered_count > 0) ? + (saved_list_index < filtered_count ? saved_list_index : 0) : -1; + } + else if (selected_index < filtered_count - 1) + selected_index++; + // else: already at the bottom of the list, the last field + } + else if (ch == '\t') // Tab: cycle focus forward + { + if (focus == FOCUS_CHECKBOX) + focus = FOCUS_DOWNLOAD_SERVER; + else if (focus == FOCUS_DOWNLOAD_SERVER) + { + focus = FOCUS_LIST; + selected_index = (filtered_count > 0) ? + (saved_list_index < filtered_count ? saved_list_index : 0) : -1; + } + else + { + saved_list_index = selected_index; + focus = FOCUS_CHECKBOX; + } + } + else if (ch == KEY_BTAB) // Shift-Tab: cycle focus backward + { + if (focus == FOCUS_CHECKBOX) + { + focus = FOCUS_LIST; + selected_index = (filtered_count > 0) ? + (saved_list_index < filtered_count ? saved_list_index : 0) : -1; + } + else if (focus == FOCUS_LIST) + { + saved_list_index = selected_index; + focus = FOCUS_DOWNLOAD_SERVER; + } + else + focus = FOCUS_CHECKBOX; + } + else if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') + { + if (focus == FOCUS_LIST && strlen(filter_buf) > 0) + { + filter_buf[strlen(filter_buf) - 1] = '\0'; + update_filter(filter_buf, filtered_keymaps, &filtered_count, + &selected_index, &list_offset); + } + else if (focus == FOCUS_DOWNLOAD_SERVER && strlen(url_buf) > 0) + url_buf[strlen(url_buf) - 1] = '\0'; + } + else if (isprint(ch)) + { + if (focus == FOCUS_LIST && strlen(filter_buf) < FILTER_BUF_LEN - 1) + { + int len = strlen(filter_buf); + filter_buf[len] = (char)ch; + filter_buf[len + 1] = '\0'; + update_filter(filter_buf, filtered_keymaps, &filtered_count, + &selected_index, &list_offset); + } + else if (focus == FOCUS_DOWNLOAD_SERVER && strlen(url_buf) < URL_BUF_LEN - 1) + { + int len = strlen(url_buf); + url_buf[len] = (char)ch; + url_buf[len + 1] = '\0'; + } + } + } + + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu.c new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu.c --- old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu.c 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu.c 2026-10-13 17:49:46.000000000 +0200 @@ -15,9 +15,7 @@ #include "nc-dialogs.h" #include "rdii-menu.h" #include "logger.h" -#include "select_keymap.h" #include "zap_partition_table.h" -#include "is_linux_vt.h" void keywait(int y, int x, const char *text, int sec) @@ -285,15 +283,15 @@ _cleanup_free_ char *image_entry = NULL; _cleanup_free_ char *target_entry = NULL; _cleanup_free_ char *mdraid_entry = NULL; - _cleanup_free_ char *keymap_entry = NULL; _cleanup_free_ char *image = NULL; _cleanup_free_ char *device = NULL; // standard device or first device of mdraid _cleanup_free_ char *mdraid = NULL; // second device for mdraid + _cleanup_free_ char *keymap = NULL; const char *options[] = { "Select Image", "Select Target", "Enable MD Raid1", - "Select Keymap", + "Settings", "System Information", "Start Installation", "Destroy Partition Table", @@ -324,6 +322,12 @@ if (!mdraid) return -ENOMEM; } + if (def_keymap) + { + keymap = strdup(def_keymap); + if (!keymap) + return -ENOMEM; + } // Adjust menu entries if (!isempty(image)) @@ -340,12 +344,6 @@ return -ENOMEM; options[1] = target_entry; } - if (!isempty(def_keymap)) - { - if (asprintf(&keymap_entry, "%s (%s)", options[3], def_keymap) < 0) - return -ENOMEM; - options[3] = keymap_entry; - } if (!isempty(image) && !isempty(device)) selected = 5; @@ -408,23 +406,8 @@ } } break; - case 3: // Select Keymap - { - if (is_linux_vt() || - show_warning_popup("Keymaps can only be configured directly on a virtual console.", - NULL, "Continue?")) - { - _cleanup_free_ char *keymap = NULL; - if (select_keymap(&keymap) == 0) - { - keymap_entry = mfree(keymap_entry); - if (asprintf(&keymap_entry, "Select Keymap (%s)", - strna(keymap)) < 0) - return -ENOMEM; - options[selected] = keymap_entry; - } - } - } + case 3: // Settings + settings(&keymap, &preserve_ssh_hostkey); break; case 4: // System Information show_sysinfo(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu.h new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu.h --- old/rdi-installer-1.1.0+git20261007.d929392/src/rdii-menu.h 2026-10-07 18:23:15.000000000 +0200 +++ new/rdi-installer-1.2.0+git20261013.6276d8c/src/rdii-menu.h 2026-10-13 17:49:46.000000000 +0200 @@ -13,6 +13,7 @@ const char *help_text); extern int select_mdraid_devices(uint64_t minsize, char **device1, char **device2); extern void select_installation_source(const char *prefill, char **ret); +extern int settings(char **keymap, bool *preserve_ssh_hostkey); extern bool url_is_valid(const char *url, const char **error); extern int show_sysinfo(void); extern bool verify_signature(const char *file, char *key, char **error);
