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);

Reply via email to