Compare commits
35 Commits
issue-1847
...
mr-2446-fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8398a7bf1 | ||
|
|
8e5276b523 | ||
|
|
66ffc74df8 | ||
|
|
ebdcce11eb | ||
|
|
8b27bb6cbd | ||
|
|
7e9af665b5 | ||
|
|
eceee2ac7f | ||
|
|
296be440cc | ||
|
|
cad4f4476e | ||
|
|
5a47a6fc8b | ||
|
|
4e09e1ff00 | ||
|
|
d29088e15d | ||
|
|
12289ef230 | ||
|
|
1093881da8 | ||
|
|
886c0c191c | ||
|
|
e357b216c9 | ||
|
|
461322bba3 | ||
|
|
bef9d6d143 | ||
|
|
8c51d35a2d | ||
|
|
700b725c3b | ||
|
|
dfc5210bab | ||
|
|
fb5af7d5da | ||
|
|
459c383be0 | ||
|
|
21ea803527 | ||
|
|
0661ae8768 | ||
|
|
0d2a56b1fc | ||
|
|
24f732091d | ||
|
|
b781e552ea | ||
|
|
11184dbe24 | ||
|
|
1aeabcbd66 | ||
|
|
6a84308797 | ||
|
|
4de8f5c88d | ||
|
|
695a972aaa | ||
|
|
a43f960075 | ||
|
|
6c25a98793 |
26
CHANGES-3.3
26
CHANGES-3.3
@@ -7,10 +7,26 @@ contributors are listed. Note that Calamares does not have a historical
|
||||
changelog -- this log starts with version 3.3.0. See CHANGES-3.2 for
|
||||
the history of the 3.2 series (2018-05 - 2022-08).
|
||||
|
||||
# 3.3.14 (2024-02-18)
|
||||
# 3.3.15 (unreleased)
|
||||
|
||||
This release contains contributions from (alphabetically by given name):
|
||||
- Adriaan de Groot
|
||||
- Erik Dubois
|
||||
- vincent PENVERN
|
||||
|
||||
## Core ##
|
||||
- Typo fixes in examples and documentation.
|
||||
|
||||
## Modules ##
|
||||
- The *keyboard* (and *keyboardq*) modules now support setting the live
|
||||
keyboard layout in a GNOME-Wayland session. (thanks vincent, #2435)
|
||||
|
||||
|
||||
# 3.3.14 (2024-02-20)
|
||||
|
||||
This release contains contributions from (alphabetically by given name):
|
||||
- Adriaan de Groot
|
||||
- Evan James
|
||||
- TNE
|
||||
- vincent PENVERN
|
||||
|
||||
@@ -19,8 +35,14 @@ This release contains contributions from (alphabetically by given name):
|
||||
consistent. At least one valid Python program would work with
|
||||
the Boost::Python bindings, but not the pybind11 bindings. A
|
||||
memory-corruption problem in the Boost::Python bindings was resolved.
|
||||
- Steps in the UI now have a hook to undo any changes they have made
|
||||
to the live system, if the user cancels the installation.
|
||||
|
||||
## Modules ##
|
||||
- *keyboard* module undoes changes to the keyboard layout if the
|
||||
user cancels the installation (returning the system to whatever
|
||||
layout was in use when Calamares started). (#2377, #2431)
|
||||
- *locale* module undoes changes to the timezone. (#2377, #2431)
|
||||
- *partition* module stores a global storage value in luksPassphrase,
|
||||
for later modules that need to manipulate the encrypted partition.
|
||||
(thanks vincent, #2424)
|
||||
@@ -118,7 +140,7 @@ This release contains contributions from (alphabetically by given name):
|
||||
- *partition* Module fixed unwanted behavior with the encryption checkbox. (thanks Aaron, #2376)
|
||||
- *umount* Correctly unmounts the root filesystem of the target. (thanks Evan)
|
||||
- *users* Supports a new `home_permissions` setting to override the
|
||||
distro's `useradd` configuration of the umask. Supports octal and rwx-style
|
||||
distribution's `useradd` configuration of the umask. Supports octal and rwx-style
|
||||
specifications of permissions. Other places that use permissions now also
|
||||
support octal and rwx-style. (#2362)
|
||||
- *welcome* Follows system styling colors (e.g. Dark Mode).
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
|
||||
|
||||
set(CALAMARES_VERSION 3.3.14)
|
||||
set(CALAMARES_RELEASE_MODE ON) # Set to ON during a release
|
||||
set(CALAMARES_VERSION 3.3.15)
|
||||
set(CALAMARES_RELEASE_MODE OFF) # Set to ON during a release
|
||||
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake)
|
||||
@@ -569,7 +569,7 @@ add_subdirectory(lang) # i18n tools
|
||||
# squashed into the example filesystem.
|
||||
#
|
||||
# To build the example distro (for use by the default, example,
|
||||
# unsquashfs module), build the target 'example-distro', eg.:
|
||||
# unsquashfs module), build the target *example-distro*, eg.:
|
||||
#
|
||||
# make example-distro
|
||||
#
|
||||
|
||||
@@ -534,7 +534,13 @@ CalamaresWindow::ensureSize( QSize size )
|
||||
void
|
||||
CalamaresWindow::closeEvent( QCloseEvent* event )
|
||||
{
|
||||
if ( ( !m_viewManager ) || m_viewManager->confirmCancelInstallation() )
|
||||
if ( m_viewManager )
|
||||
{
|
||||
m_viewManager->quit();
|
||||
// If it didn't actually exit, eat the event to ignore close
|
||||
event->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
event->accept();
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
@@ -543,8 +549,4 @@ CalamaresWindow::closeEvent( QCloseEvent* event )
|
||||
QApplication::exit( EXIT_SUCCESS );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
return std::tie( lhs.m_region, lhs.m_zone ) == std::tie( rhs.m_region, rhs.m_zone );
|
||||
}
|
||||
|
||||
QString asString() const { return isValid() ? region() + QChar( '/' ) + zone() : QString(); }
|
||||
|
||||
private:
|
||||
QString m_region;
|
||||
QString m_zone;
|
||||
@@ -68,13 +70,13 @@ private:
|
||||
inline QDebug&
|
||||
operator<<( QDebug&& s, const RegionZonePair& tz )
|
||||
{
|
||||
return s << tz.region() << '/' << tz.zone();
|
||||
return s << tz.asString();
|
||||
}
|
||||
|
||||
inline QDebug&
|
||||
operator<<( QDebug& s, const RegionZonePair& tz )
|
||||
{
|
||||
return s << tz.region() << '/' << tz.zone();
|
||||
return s << tz.asString();
|
||||
}
|
||||
|
||||
/** @brief Splits a region/zone string into a pair.
|
||||
|
||||
@@ -489,17 +489,31 @@ ViewManager::back()
|
||||
void
|
||||
ViewManager::quit()
|
||||
{
|
||||
if ( confirmCancelInstallation() )
|
||||
const auto r = confirmCancelInstallation();
|
||||
if ( r == Confirmation::Continue )
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
QApplication::quit();
|
||||
#else
|
||||
QApplication::exit( EXIT_SUCCESS );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if ( r == Confirmation::CancelInstallation )
|
||||
{
|
||||
// Cancel view steps in reverse
|
||||
for ( int i = m_currentStep; i >= 0; --i )
|
||||
{
|
||||
auto* step = m_steps.at( i );
|
||||
cDebug() << "Cancelling view step" << step->moduleInstanceKey();
|
||||
step->onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
QApplication::quit();
|
||||
#else
|
||||
QApplication::exit( EXIT_SUCCESS );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ViewManager::Confirmation
|
||||
ViewManager::confirmCancelInstallation()
|
||||
{
|
||||
const auto* const settings = Calamares::Settings::instance();
|
||||
@@ -507,17 +521,17 @@ ViewManager::confirmCancelInstallation()
|
||||
// When we're at the very end, then it's always OK to exit.
|
||||
if ( isAtVeryEnd( m_steps, m_currentStep ) )
|
||||
{
|
||||
return true;
|
||||
return Confirmation::EndOfInstallation;
|
||||
}
|
||||
|
||||
// Not at the very end, cancel/quit might be disabled
|
||||
if ( settings->disableCancel() )
|
||||
{
|
||||
return false;
|
||||
return Confirmation::Continue;
|
||||
}
|
||||
if ( settings->disableCancelDuringExec() && stepIsExecute( m_steps, m_currentStep ) )
|
||||
{
|
||||
return false;
|
||||
return Confirmation::Continue;
|
||||
}
|
||||
|
||||
// Otherwise, confirm cancel/quit.
|
||||
@@ -530,7 +544,7 @@ ViewManager::confirmCancelInstallation()
|
||||
mb.setDefaultButton( QMessageBox::No );
|
||||
Calamares::fixButtonLabels( &mb );
|
||||
int response = mb.exec();
|
||||
return response == QMessageBox::Yes;
|
||||
return ( response == QMessageBox::Yes ) ? Confirmation::CancelInstallation : Confirmation::Continue;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -101,14 +101,21 @@ public:
|
||||
*/
|
||||
int currentStepIndex() const;
|
||||
|
||||
enum class Confirmation
|
||||
{
|
||||
Continue, // User rejects cancel / close question
|
||||
CancelInstallation, // User accepts cancel / close question
|
||||
EndOfInstallation, // There was no question because the installation was already done
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Called when "Cancel" is clicked; asks for confirmation.
|
||||
* Other means of closing Calamares also call this method, e.g. alt-F4.
|
||||
* At the end of installation, no confirmation is asked.
|
||||
*
|
||||
* @return @c true if the user confirms closing the window.
|
||||
* @return a Confirmation value, @c Unasked if the installation is complete
|
||||
*/
|
||||
bool confirmCancelInstallation();
|
||||
Confirmation confirmCancelInstallation();
|
||||
|
||||
Qt::Orientations panelSides() const { return m_panelSides; }
|
||||
void setPanelSides( Qt::Orientations panelSides ) { m_panelSides = panelSides; }
|
||||
|
||||
@@ -45,6 +45,11 @@ ViewStep::onLeave()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ViewStep::onCancel()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ViewStep::next()
|
||||
{
|
||||
|
||||
@@ -168,6 +168,15 @@ public:
|
||||
*/
|
||||
virtual RequirementsList checkRequirements();
|
||||
|
||||
/**
|
||||
* @brief Called when the user cancels the installation
|
||||
*
|
||||
* View steps are expected to leave the system unchanged when
|
||||
* the installation is cancelled. The default implementation
|
||||
* does nothing.
|
||||
*/
|
||||
virtual void onCancel();
|
||||
|
||||
signals:
|
||||
/// @brief Tells the viewmanager to enable the *next* button according to @p status
|
||||
void nextStatusChanged( bool status );
|
||||
|
||||
@@ -81,3 +81,6 @@ efiBootMgr: "efibootmgr"
|
||||
# to add another module to optionally install the fallback on those
|
||||
# boards that need it.
|
||||
installEFIFallback: true
|
||||
|
||||
# Optionally install both BIOS and UEFI GRUB bootloaders.
|
||||
installHybridGRUB: false
|
||||
|
||||
@@ -24,3 +24,4 @@ properties:
|
||||
|
||||
efiBootloaderId: { type: string }
|
||||
installEFIFallback: { type: boolean }
|
||||
installHybridGRUB: { type: boolean }
|
||||
|
||||
@@ -28,7 +28,7 @@ import subprocess
|
||||
|
||||
import libcalamares
|
||||
|
||||
from libcalamares.utils import check_target_env_call
|
||||
from libcalamares.utils import check_target_env_call, check_target_env_output
|
||||
|
||||
import gettext
|
||||
|
||||
@@ -605,7 +605,7 @@ def run_grub_mkconfig(partitions, output_file):
|
||||
check_target_env_call([libcalamares.job.configuration["grubMkconfig"], "-o", output_file])
|
||||
|
||||
|
||||
def run_grub_install(fw_type, partitions, efi_directory):
|
||||
def run_grub_install(fw_type, partitions, efi_directory, install_hybrid_grub):
|
||||
"""
|
||||
Runs grub-install in the target environment
|
||||
|
||||
@@ -637,33 +637,52 @@ def run_grub_install(fw_type, partitions, efi_directory):
|
||||
"--bootloader-id=" + efi_bootloader_id,
|
||||
"--force"])
|
||||
else:
|
||||
assert efi_directory is None
|
||||
if libcalamares.globalstorage.value("bootLoader") is None:
|
||||
if efi_directory is not None and not install_hybrid_grub:
|
||||
libcalamares.utils.warning(_("Cannot install BIOS bootloader on UEFI installation when install_hybrid_grub is 'False'!"))
|
||||
return
|
||||
|
||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
||||
if boot_loader["installPath"] is None:
|
||||
return
|
||||
if libcalamares.globalstorage.value("bootLoader") is None:
|
||||
efi_install_path = libcalamares.globalstorage.value("efiSystemPartition")
|
||||
if efi_install_path is None or efi_install_path == "":
|
||||
efi_install_path = "/boot/efi"
|
||||
find_esp_disk_command = f"lsblk -o PKNAME \"$(df --output=source '{efi_install_path}' | tail -n1)\""
|
||||
boot_loader_install_path = check_target_env_output(["sh", "-c", find_esp_disk_command]).strip()
|
||||
if not "\n" in boot_loader_install_path:
|
||||
libcalamares.utils.warning(_("Cannot find the drive containing the EFI system partition!"))
|
||||
return
|
||||
boot_loader_install_path = "/dev/" + boot_loader_install_path.split("\n")[1]
|
||||
else:
|
||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
||||
boot_loader_install_path = boot_loader["installPath"]
|
||||
if boot_loader_install_path is None:
|
||||
return
|
||||
|
||||
# boot_loader_install_path points to the physical disk to install GRUB
|
||||
# to. It should start with "/dev/", and be at least as long as the
|
||||
# string "/dev/sda".
|
||||
if not boot_loader_install_path.startswith("/dev/") or len(boot_loader_install_path) < 8:
|
||||
raise ValueError(f"boot_loader_install_path contains unexpected value '{boot_loader_install_path}'")
|
||||
|
||||
if is_zfs:
|
||||
check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 "
|
||||
+ libcalamares.job.configuration["grubInstall"]
|
||||
+ " --target=i386-pc --recheck --force "
|
||||
+ boot_loader["installPath"]])
|
||||
+ boot_loader_install_path])
|
||||
else:
|
||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
||||
"--target=i386-pc",
|
||||
"--recheck",
|
||||
"--force",
|
||||
boot_loader["installPath"]])
|
||||
boot_loader_install_path])
|
||||
|
||||
|
||||
def install_grub(efi_directory, fw_type):
|
||||
def install_grub(efi_directory, fw_type, install_hybrid_grub):
|
||||
"""
|
||||
Installs grub as bootloader, either in pc or efi mode.
|
||||
|
||||
:param efi_directory:
|
||||
:param fw_type:
|
||||
:param install_hybrid_grub:
|
||||
"""
|
||||
# get the partition from global storage
|
||||
partitions = libcalamares.globalstorage.value("partitions")
|
||||
@@ -671,8 +690,12 @@ def install_grub(efi_directory, fw_type):
|
||||
libcalamares.utils.warning(_("Failed to install grub, no partitions defined in global storage"))
|
||||
return
|
||||
|
||||
if fw_type == "efi":
|
||||
if fw_type != "bios" and fw_type != "efi":
|
||||
raise ValueError("fw_type must be 'bios' or 'efi'")
|
||||
|
||||
if fw_type == "efi" or install_hybrid_grub:
|
||||
libcalamares.utils.debug("Bootloader: grub (efi)")
|
||||
libcalamares.utils.debug(f"install_hybrid_grub: {install_hybrid_grub}")
|
||||
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||
install_efi_directory = installation_root_path + efi_directory
|
||||
|
||||
@@ -683,7 +706,7 @@ def install_grub(efi_directory, fw_type):
|
||||
|
||||
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
||||
|
||||
run_grub_install(fw_type, partitions, efi_directory)
|
||||
run_grub_install("efi", partitions, efi_directory, install_hybrid_grub)
|
||||
|
||||
# VFAT is weird, see issue CAL-385
|
||||
install_efi_directory_firmware = (vfat_correct_case(
|
||||
@@ -706,15 +729,36 @@ def install_grub(efi_directory, fw_type):
|
||||
libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(fallback, "<unset>")))
|
||||
if libcalamares.job.configuration.get(fallback, True):
|
||||
libcalamares.utils.debug(" .. installing '{!s}' fallback firmware".format(efi_boot_file))
|
||||
efi_file_source = os.path.join(install_efi_directory_firmware,
|
||||
efi_bootloader_id,
|
||||
efi_grub_file)
|
||||
efi_file_target = os.path.join(install_efi_boot_directory, efi_boot_file)
|
||||
# Try to use a distro-specific installation routine first
|
||||
distro_name = ""
|
||||
with open(os.path.join(installation_root_path,
|
||||
"/etc/os-release"), "r") as os_release_file:
|
||||
for os_release_line in os_release_file:
|
||||
if os_release_line.startswith("NAME="):
|
||||
distro_name = os_release_line.split("=", 1)[1].strip("\"\n")
|
||||
match distro_name:
|
||||
case "Debian GNU/Linux":
|
||||
efi_deb_pkg_arch = None
|
||||
match efi_target:
|
||||
# Note: No loongarch64 EFI support in Debian.
|
||||
case "i386-efi":
|
||||
efi_deb_pkg_arch = "ia32"
|
||||
case "arm64-efi":
|
||||
efi_deb_pkg_arch = "arm64"
|
||||
case "x86_64-efi":
|
||||
efi_deb_pkg_arch = "amd64"
|
||||
if efi_deb_pkg_arch is not None:
|
||||
check_target_env_call(["sh", "-c", f"echo 'grub-efi-{efi_deb_pkg_arch} grub2/force_efi_extra_removable boolean true' | debconf-set-selections && dpkg-reconfigure --default-priority grub-efi-{efi_deb_pkg_arch}"])
|
||||
case _:
|
||||
efi_file_source = os.path.join(install_efi_directory_firmware,
|
||||
efi_bootloader_id,
|
||||
efi_grub_file)
|
||||
efi_file_target = os.path.join(install_efi_boot_directory, efi_boot_file)
|
||||
|
||||
shutil.copy2(efi_file_source, efi_file_target)
|
||||
else:
|
||||
shutil.copy2(efi_file_source, efi_file_target)
|
||||
if fw_type == "bios" or install_hybrid_grub:
|
||||
libcalamares.utils.debug("Bootloader: grub (bios)")
|
||||
run_grub_install(fw_type, partitions, None)
|
||||
run_grub_install("bios", partitions, efi_directory, install_hybrid_grub)
|
||||
|
||||
run_grub_mkconfig(partitions, libcalamares.job.configuration["grubCfg"])
|
||||
|
||||
@@ -844,7 +888,7 @@ def install_refind(efi_directory):
|
||||
update_refind_config(efi_directory, installation_root_path)
|
||||
|
||||
|
||||
def prepare_bootloader(fw_type):
|
||||
def prepare_bootloader(fw_type, install_hybrid_grub):
|
||||
"""
|
||||
Prepares bootloader.
|
||||
Based on value 'efi_boot_loader', it either calls systemd-boot
|
||||
@@ -868,8 +912,8 @@ def prepare_bootloader(fw_type):
|
||||
try:
|
||||
efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
|
||||
except KeyError:
|
||||
if fw_type == "efi":
|
||||
libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI "
|
||||
if fw_type == "efi" or install_hybrid_grub:
|
||||
libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI-enabled "
|
||||
"system, bootloader not installed")
|
||||
return
|
||||
else:
|
||||
@@ -894,7 +938,7 @@ def prepare_bootloader(fw_type):
|
||||
elif efi_boot_loader == "refind" and fw_type == "efi":
|
||||
install_refind(efi_directory)
|
||||
elif efi_boot_loader == "grub" or fw_type != "efi":
|
||||
install_grub(efi_directory, fw_type)
|
||||
install_grub(efi_directory, fw_type, install_hybrid_grub)
|
||||
else:
|
||||
libcalamares.utils.debug("WARNING: the combination of "
|
||||
"boot-loader '{!s}' and firmware '{!s}' "
|
||||
@@ -909,8 +953,15 @@ def run():
|
||||
"""
|
||||
|
||||
fw_type = libcalamares.globalstorage.value("firmwareType")
|
||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
||||
|
||||
if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi":
|
||||
install_hybrid_grub = libcalamares.job.configuration.get("installHybridGRUB", False)
|
||||
efi_boot_loader = libcalamares.job.configuration.get("efiBootLoader", "")
|
||||
|
||||
if install_hybrid_grub == True and efi_boot_loader != "grub":
|
||||
raise ValueError(f"efi_boot_loader '{efi_boot_loader}' is illegal when install_hybrid_grub is 'true'!")
|
||||
|
||||
if boot_loader is None and fw_type != "efi":
|
||||
libcalamares.utils.warning("Non-EFI system, and no bootloader is set.")
|
||||
return None
|
||||
|
||||
@@ -923,7 +974,7 @@ def run():
|
||||
return None
|
||||
|
||||
try:
|
||||
prepare_bootloader(fw_type)
|
||||
prepare_bootloader(fw_type, install_hybrid_grub)
|
||||
except subprocess.CalledProcessError as e:
|
||||
libcalamares.utils.warning(str(e))
|
||||
libcalamares.utils.debug("stdout:" + str(e.stdout))
|
||||
|
||||
@@ -22,7 +22,7 @@ overwrite: false
|
||||
|
||||
# If set to true, prefer to write files in /etc/default/grub.d/
|
||||
# rather than the single file /etc/default/grub. If this is set,
|
||||
# Calamares will write /etc/default/grub.d/00Calamares instead.
|
||||
# Calamares will write /etc/default/grub.d/00calamares.cfg instead.
|
||||
prefer_grub_d: false
|
||||
|
||||
# If set to true, an **existing** setting for GRUB_DISTRIBUTOR is
|
||||
|
||||
@@ -45,7 +45,7 @@ def get_grub_config_path(root_mount_point):
|
||||
possible_dir = os.path.join(root_mount_point, "etc/default/grub.d")
|
||||
if os.path.exists(possible_dir) and os.path.isdir(possible_dir):
|
||||
default_dir = possible_dir
|
||||
default_config_file = "00calamares"
|
||||
default_config_file = "00calamares.cfg"
|
||||
|
||||
if not os.path.exists(default_dir):
|
||||
try:
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct BasicLayoutInfo
|
||||
{
|
||||
QString selectedLayout;
|
||||
QString selectedModel;
|
||||
QString selectedVariant;
|
||||
QString selectedGroup;
|
||||
};
|
||||
|
||||
struct AdditionalLayoutInfo
|
||||
{
|
||||
QString additionalLayout;
|
||||
|
||||
@@ -169,7 +169,7 @@ Config::Config( QObject* parent )
|
||||
&KeyboardModelsModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedModel = m_keyboardModelsModel->key( index );
|
||||
m_current.selectedModel = m_keyboardModelsModel->key( index );
|
||||
somethingChanged();
|
||||
} );
|
||||
|
||||
@@ -177,7 +177,7 @@ Config::Config( QObject* parent )
|
||||
&KeyboardLayoutModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedLayout = m_keyboardLayoutsModel->item( index ).first;
|
||||
m_current.selectedLayout = m_keyboardLayoutsModel->item( index ).first;
|
||||
updateVariants( QPersistentModelIndex( m_keyboardLayoutsModel->index( index ) ) );
|
||||
emit prettyStatusChanged();
|
||||
} );
|
||||
@@ -186,14 +186,14 @@ Config::Config( QObject* parent )
|
||||
&KeyboardVariantsModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedVariant = m_keyboardVariantsModel->key( index );
|
||||
m_current.selectedVariant = m_keyboardVariantsModel->key( index );
|
||||
somethingChanged();
|
||||
} );
|
||||
connect( m_KeyboardGroupSwitcherModel,
|
||||
&KeyboardGroupsSwitchersModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedGroup = m_KeyboardGroupSwitcherModel->key( index );
|
||||
m_current.selectedGroup = m_KeyboardGroupSwitcherModel->key( index );
|
||||
somethingChanged();
|
||||
} );
|
||||
|
||||
@@ -207,10 +207,10 @@ Config::Config( QObject* parent )
|
||||
this,
|
||||
&Config::selectionChange );
|
||||
|
||||
m_selectedModel = m_keyboardModelsModel->key( m_keyboardModelsModel->currentIndex() );
|
||||
m_selectedLayout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).first;
|
||||
m_selectedVariant = m_keyboardVariantsModel->key( m_keyboardVariantsModel->currentIndex() );
|
||||
m_selectedGroup = m_KeyboardGroupSwitcherModel->key( m_KeyboardGroupSwitcherModel->currentIndex() );
|
||||
m_current.selectedModel = m_keyboardModelsModel->key( m_keyboardModelsModel->currentIndex() );
|
||||
m_current.selectedLayout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).first;
|
||||
m_current.selectedVariant = m_keyboardVariantsModel->key( m_keyboardVariantsModel->currentIndex() );
|
||||
m_current.selectedGroup = m_KeyboardGroupSwitcherModel->key( m_KeyboardGroupSwitcherModel->currentIndex() );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -224,38 +224,56 @@ Config::somethingChanged()
|
||||
emit prettyStatusChanged();
|
||||
}
|
||||
|
||||
void
|
||||
Config::apply()
|
||||
static void
|
||||
applyXkb( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra )
|
||||
{
|
||||
if ( m_configureXkb )
|
||||
QStringList basicArguments = xkbmap_model_args( settings.selectedModel );
|
||||
if ( !extra.additionalLayout.isEmpty() )
|
||||
{
|
||||
applyXkb();
|
||||
if ( !settings.selectedGroup.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = "grp:" + settings.selectedGroup;
|
||||
}
|
||||
|
||||
if ( extra.groupSwitcher.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = xkbmap_query_grp_option();
|
||||
}
|
||||
if ( extra.groupSwitcher.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = "grp:alt_shift_toggle";
|
||||
}
|
||||
|
||||
basicArguments.append(
|
||||
xkbmap_layout_args_with_group_switch( { extra.additionalLayout, settings.selectedLayout },
|
||||
{ extra.additionalVariant, settings.selectedVariant },
|
||||
extra.groupSwitcher ) );
|
||||
QProcess::execute( "setxkbmap", basicArguments );
|
||||
|
||||
cDebug() << "xkbmap selection changed to: " << settings.selectedLayout << '-' << settings.selectedVariant
|
||||
<< "(added " << extra.additionalLayout << "-" << extra.additionalVariant
|
||||
<< " since current layout is not ASCII-capable)";
|
||||
}
|
||||
if ( m_configureLocale1 )
|
||||
else
|
||||
{
|
||||
applyLocale1();
|
||||
basicArguments.append( xkbmap_layout_args( settings.selectedLayout, settings.selectedVariant ) );
|
||||
QProcess::execute( "setxkbmap", basicArguments );
|
||||
cDebug() << "xkbmap selection changed to: " << settings.selectedLayout << '-' << settings.selectedVariant;
|
||||
}
|
||||
if ( m_configureKWin )
|
||||
{
|
||||
applyKWin();
|
||||
}
|
||||
// Writing /etc/ files is not needed "live"
|
||||
}
|
||||
|
||||
void
|
||||
Config::applyLocale1()
|
||||
static void
|
||||
applyLocale1( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra )
|
||||
{
|
||||
m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout );
|
||||
|
||||
QString layout = m_selectedLayout;
|
||||
QString variant = m_selectedVariant;
|
||||
QString layout = settings.selectedLayout;
|
||||
QString variant = settings.selectedVariant;
|
||||
QString option;
|
||||
|
||||
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
|
||||
if ( !extra.additionalLayout.isEmpty() )
|
||||
{
|
||||
layout = m_additionalLayoutInfo.additionalLayout + "," + layout;
|
||||
variant = m_additionalLayoutInfo.additionalVariant + "," + variant;
|
||||
option = m_additionalLayoutInfo.groupSwitcher;
|
||||
layout = extra.additionalLayout + "," + layout;
|
||||
variant = extra.additionalVariant + "," + variant;
|
||||
option = extra.groupSwitcher;
|
||||
}
|
||||
|
||||
QDBusInterface locale1( "org.freedesktop.locale1",
|
||||
@@ -270,7 +288,8 @@ Config::applyLocale1()
|
||||
|
||||
// Using convert=true, this also updates the VConsole config
|
||||
{
|
||||
QDBusReply< void > r = locale1.call( "SetX11Keyboard", layout, m_selectedModel, variant, option, true, false );
|
||||
QDBusReply< void > r
|
||||
= locale1.call( "SetX11Keyboard", layout, settings.selectedModel, variant, option, true, false );
|
||||
if ( !r.isValid() )
|
||||
{
|
||||
cWarning() << "Could not set keyboard config through org.freedesktop.locale1.X11Keyboard." << r.error();
|
||||
@@ -278,47 +297,6 @@ Config::applyLocale1()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::applyXkb()
|
||||
{
|
||||
m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout );
|
||||
|
||||
QStringList basicArguments = xkbmap_model_args( m_selectedModel );
|
||||
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
|
||||
{
|
||||
if ( !m_selectedGroup.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = "grp:" + m_selectedGroup;
|
||||
}
|
||||
|
||||
if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = xkbmap_query_grp_option();
|
||||
}
|
||||
if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = "grp:alt_shift_toggle";
|
||||
}
|
||||
|
||||
basicArguments.append(
|
||||
xkbmap_layout_args_with_group_switch( { m_additionalLayoutInfo.additionalLayout, m_selectedLayout },
|
||||
{ m_additionalLayoutInfo.additionalVariant, m_selectedVariant },
|
||||
m_additionalLayoutInfo.groupSwitcher ) );
|
||||
QProcess::execute( "setxkbmap", basicArguments );
|
||||
|
||||
cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant << "(added "
|
||||
<< m_additionalLayoutInfo.additionalLayout << "-" << m_additionalLayoutInfo.additionalVariant
|
||||
<< " since current layout is not ASCII-capable)";
|
||||
}
|
||||
else
|
||||
{
|
||||
basicArguments.append( xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) );
|
||||
QProcess::execute( "setxkbmap", basicArguments );
|
||||
cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant;
|
||||
}
|
||||
m_applyTimer.stop();
|
||||
}
|
||||
|
||||
// In a config-file's list of lines, replace lines <key>=<something> by <key>=<value>
|
||||
static void
|
||||
replaceKey( QStringList& content, const QString& key, const QString& value )
|
||||
@@ -368,21 +346,21 @@ rewriteKWin( const QString& path, const QString& model, const QString& layouts,
|
||||
}
|
||||
|
||||
void
|
||||
Config::applyKWin()
|
||||
applyKWin( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra )
|
||||
{
|
||||
const auto paths = QStandardPaths::standardLocations( QStandardPaths::ConfigLocation );
|
||||
|
||||
auto join = [ &additional = m_additionalLayoutInfo.additionalLayout ]( const QString& s1, const QString& s2 )
|
||||
auto join = [ &additional = extra.additionalLayout ]( const QString& s1, const QString& s2 )
|
||||
{ return additional.isEmpty() ? s1 : QStringLiteral( "%1,%2" ).arg( s1, s2 ); };
|
||||
|
||||
const QString layouts = join( m_selectedLayout, m_additionalLayoutInfo.additionalLayout );
|
||||
const QString variants = join( m_selectedVariant, m_additionalLayoutInfo.additionalVariant );
|
||||
const QString layouts = join( settings.selectedLayout, extra.additionalLayout );
|
||||
const QString variants = join( settings.selectedVariant, extra.additionalVariant );
|
||||
|
||||
bool updated = false;
|
||||
for ( const auto& path : paths )
|
||||
{
|
||||
const QString candidate = path + QStringLiteral( "/kxkbrc" );
|
||||
if ( rewriteKWin( candidate, m_selectedModel, layouts, variants ) )
|
||||
if ( rewriteKWin( candidate, settings.selectedModel, layouts, variants ) )
|
||||
{
|
||||
updated = true;
|
||||
break;
|
||||
@@ -397,6 +375,98 @@ Config::applyKWin()
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
squareBracketedList( const QStringList& l )
|
||||
{
|
||||
return QStringLiteral( "[%1]" ).arg( l.join( ", " ) );
|
||||
}
|
||||
|
||||
// Fpr a layout and variant, returns a string like "('xkb', 'uk+latin1')"
|
||||
QString
|
||||
concatLayoutAndVariant( const QString& layout, const QString& variant )
|
||||
{
|
||||
return QStringLiteral( "('xkb', '%1')" ).arg( variant.isEmpty() ? layout : ( layout + '+' + variant ) );
|
||||
}
|
||||
|
||||
// Seem's keyboard settings don't work anymore with setxkbkeyboard with Gnome and Wayland
|
||||
// use applyGnome() to use gsettings specific command
|
||||
void
|
||||
applyGnome( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra )
|
||||
{
|
||||
static constexpr int expectedUID = 1000; // Assume this is the live-cd user-id
|
||||
const QString sudoUser
|
||||
= QStringLiteral( "#%1" ).arg( expectedUID ); // GNU sudo can use '-u #nnn' with a literal '#' and numeric UID
|
||||
const QString dbusPath = QStringLiteral( "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%1/bus" ).arg( expectedUID );
|
||||
const QString sudo = QStringLiteral( "sudo" );
|
||||
// clang-format off
|
||||
// These are arguments to sudo to run gsettings to set something on input-sources
|
||||
const QStringList sudoArguments{
|
||||
"-u", sudoUser, // Run as numeric UID
|
||||
dbusPath, // Set environment to pick up live user session bus
|
||||
"gsettings", "set", "org.gnome.desktop.input-sources" // Command, still needs a key and a value after this
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
QStringList sources { concatLayoutAndVariant( settings.selectedLayout, settings.selectedVariant ) };
|
||||
|
||||
// Case for ukrainian homophonic keyboard for exemple
|
||||
// need to configure 2 keyboards and a toggle key
|
||||
// gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'uk+latin1'), ('xkb','en')]"
|
||||
// gsettings set org.gnome.desktop.input-sources xkb-options "['grp:lalt_lshift_toggle']"
|
||||
if ( !extra.additionalLayout.isEmpty() )
|
||||
{
|
||||
// Get a reasonable value for the group switcher, defaulting to alt_shift_toggle if nothing else is set
|
||||
if ( !settings.selectedGroup.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = "grp:" + settings.selectedGroup;
|
||||
}
|
||||
if ( extra.groupSwitcher.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = xkbmap_query_grp_option();
|
||||
}
|
||||
if ( extra.groupSwitcher.isEmpty() )
|
||||
{
|
||||
extra.groupSwitcher = "grp:alt_shift_toggle";
|
||||
}
|
||||
|
||||
const QString xkbOptionsValue = QStringLiteral( "['%1']" ).arg( extra.groupSwitcher );
|
||||
const QStringList xkbOptionsCommand = QStringList( sudoArguments ) << "xkb-options" << xkbOptionsValue;
|
||||
QProcess::execute( "sudo", xkbOptionsCommand );
|
||||
cDebug() << "Executed: sudo" << xkbOptionsCommand;
|
||||
|
||||
// And add additional layout to the sources-list
|
||||
sources.append( concatLayoutAndVariant( extra.additionalLayout, extra.additionalVariant ) );
|
||||
}
|
||||
|
||||
const QStringList sourcesCommand = QStringList( sudoArguments ) << "sources" << squareBracketedList( sources );
|
||||
QProcess::execute( "sudo", sourcesCommand );
|
||||
cDebug() << "Executed: sudo" << sourcesCommand;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Config::apply()
|
||||
{
|
||||
m_additionalLayoutInfo = getAdditionalLayoutInfo( m_current.selectedLayout );
|
||||
if ( m_configureXkb )
|
||||
{
|
||||
applyXkb( m_current, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureLocale1 )
|
||||
{
|
||||
applyLocale1( m_current, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureKWin )
|
||||
{
|
||||
applyKWin( m_current, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureGnome )
|
||||
{
|
||||
applyGnome( m_current, m_additionalLayoutInfo );
|
||||
}
|
||||
m_applyTimer.stop();
|
||||
// Writing /etc/ files is not needed "live"
|
||||
}
|
||||
|
||||
KeyboardModelsModel*
|
||||
Config::keyboardModels() const
|
||||
@@ -574,6 +644,30 @@ Config::detectCurrentKeyboardLayout()
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The models have updated the m_current settings, copy them
|
||||
m_original = m_current;
|
||||
}
|
||||
|
||||
void
|
||||
Config::cancel()
|
||||
{
|
||||
const auto extra = getAdditionalLayoutInfo( m_original.selectedLayout );
|
||||
if ( m_configureXkb )
|
||||
{
|
||||
applyXkb( m_original, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureLocale1 )
|
||||
{
|
||||
applyLocale1( m_original, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureKWin )
|
||||
{
|
||||
applyKWin( m_original, m_additionalLayoutInfo );
|
||||
}
|
||||
if ( m_configureGnome )
|
||||
{
|
||||
applyGnome( m_original, m_additionalLayoutInfo );
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
@@ -599,9 +693,9 @@ Config::createJobs()
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
|
||||
Calamares::Job* j = new SetKeyboardLayoutJob( m_selectedModel,
|
||||
m_selectedLayout,
|
||||
m_selectedVariant,
|
||||
Calamares::Job* j = new SetKeyboardLayoutJob( m_current.selectedModel,
|
||||
m_current.selectedLayout,
|
||||
m_current.selectedVariant,
|
||||
m_additionalLayoutInfo,
|
||||
m_xOrgConfFileName,
|
||||
m_convertedKeymapPath,
|
||||
@@ -748,10 +842,10 @@ void
|
||||
Config::finalize()
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( !m_selectedLayout.isEmpty() )
|
||||
if ( !m_current.selectedLayout.isEmpty() )
|
||||
{
|
||||
gs->insert( "keyboardLayout", m_selectedLayout );
|
||||
gs->insert( "keyboardVariant", m_selectedVariant ); //empty means default variant
|
||||
gs->insert( "keyboardLayout", m_current.selectedLayout );
|
||||
gs->insert( "keyboardVariant", m_current.selectedVariant ); //empty means default variant
|
||||
|
||||
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
|
||||
{
|
||||
@@ -802,6 +896,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
bool bogus = false;
|
||||
const auto configureItems = getSubMap( configurationMap, "configure", bogus );
|
||||
m_configureKWin = getBool( configureItems, "kwin", false );
|
||||
m_configureGnome = getBool( configureItems, "gnome", false );
|
||||
|
||||
m_guessLayout = getBool( configurationMap, "guessLayout", true );
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ public:
|
||||
/// @brief When leaving the page, write to GS
|
||||
void finalize();
|
||||
|
||||
/// @brief Restore the system to whatever layout was in use when detectCurrentKeyboardLayout() was called
|
||||
void cancel();
|
||||
|
||||
static AdditionalLayoutInfo getAdditionalLayoutInfo( const QString& layout );
|
||||
|
||||
/* A model is a physical configuration of a keyboard, e.g. 105-key PC
|
||||
@@ -94,9 +97,6 @@ private:
|
||||
*/
|
||||
void somethingChanged();
|
||||
void apply();
|
||||
void applyLocale1();
|
||||
void applyXkb();
|
||||
void applyKWin();
|
||||
|
||||
void getCurrentKeyboardLayoutXkb( QString& currentLayout, QString& currentVariant, QString& currentModel );
|
||||
void getCurrentKeyboardLayoutLocale1( QString& currentLayout, QString& currentVariant, QString& currentModel );
|
||||
@@ -106,10 +106,8 @@ private:
|
||||
KeyboardVariantsModel* m_keyboardVariantsModel;
|
||||
KeyboardGroupsSwitchersModel* m_KeyboardGroupSwitcherModel;
|
||||
|
||||
QString m_selectedLayout;
|
||||
QString m_selectedModel;
|
||||
QString m_selectedVariant;
|
||||
QString m_selectedGroup;
|
||||
BasicLayoutInfo m_current;
|
||||
BasicLayoutInfo m_original;
|
||||
|
||||
// Layout (and corresponding info) added if current one doesn't support ASCII (e.g. Russian or Japanese)
|
||||
AdditionalLayoutInfo m_additionalLayoutInfo;
|
||||
@@ -123,6 +121,7 @@ private:
|
||||
bool m_configureEtcDefaultKeyboard = true;
|
||||
bool m_configureLocale1 = false;
|
||||
bool m_configureKWin = false;
|
||||
bool m_configureGnome = false;
|
||||
bool m_guessLayout = false;
|
||||
|
||||
// The state determines whether we guess settings or preserve them:
|
||||
|
||||
@@ -104,6 +104,11 @@ KeyboardViewStep::onLeave()
|
||||
m_config->finalize();
|
||||
}
|
||||
|
||||
void
|
||||
KeyboardViewStep::onCancel()
|
||||
{
|
||||
m_config->cancel();
|
||||
}
|
||||
|
||||
void
|
||||
KeyboardViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
|
||||
void onActivate() override;
|
||||
void onLeave() override;
|
||||
void onCancel() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
|
||||
@@ -43,3 +43,5 @@ configure:
|
||||
# compositor KWin with command-line argument `--locale1`. That
|
||||
# argument makes this configuration option unnecessary.
|
||||
kwin: false
|
||||
# Configure keyboard when using Wayland with Gnome on Ubuntu 24.10+
|
||||
gnome: false
|
||||
|
||||
@@ -80,6 +80,12 @@ KeyboardQmlViewStep::onLeave()
|
||||
m_config->finalize();
|
||||
}
|
||||
|
||||
void
|
||||
KeyboardQmlViewStep::onCancel()
|
||||
{
|
||||
m_config->cancel();
|
||||
}
|
||||
|
||||
QObject*
|
||||
KeyboardQmlViewStep::getConfig()
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
|
||||
void onActivate() override;
|
||||
void onLeave() override;
|
||||
void onCancel() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
QObject* getConfig() override;
|
||||
|
||||
@@ -378,7 +378,7 @@ Config::currentLocationStatus() const
|
||||
{
|
||||
if ( m_currentLocation )
|
||||
{
|
||||
return tr( "Set timezone to %1.", "@action" ).arg( currentTimezoneName());
|
||||
return tr( "Set timezone to %1.", "@action" ).arg( currentTimezoneName() );
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
@@ -513,6 +513,8 @@ getGeoIP( const QVariantMap& configurationMap, std::unique_ptr< Calamares::GeoIP
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
m_originalTimezone = Calamares::GeoIP::splitTZString( QTimeZone::systemTimeZoneId() );
|
||||
|
||||
getLocaleGenLines( configurationMap, m_localeGenLines );
|
||||
getAdjustLiveTimezone( configurationMap, m_adjustLiveTimezone );
|
||||
getStartingTimezone( configurationMap, m_startingTimezone );
|
||||
@@ -588,3 +590,12 @@ Config::completeGeoIP()
|
||||
m_geoipWatcher.reset();
|
||||
m_geoip.reset();
|
||||
}
|
||||
|
||||
void
|
||||
Config::cancel()
|
||||
{
|
||||
if ( m_adjustLiveTimezone && m_originalTimezone.isValid() )
|
||||
{
|
||||
QProcess::execute( "timedatectl", { "set-timezone", m_originalTimezone.asString() } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,10 +83,12 @@ public:
|
||||
|
||||
const Calamares::Locale::TimeZoneData* currentLocation() const { return m_currentLocation; }
|
||||
|
||||
|
||||
/// Special case, set location from starting timezone if not already set
|
||||
void setCurrentLocation();
|
||||
|
||||
/// Restores original timezone, if any
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
Calamares::Locale::TimeZoneData* currentLocation_c() const
|
||||
{
|
||||
@@ -176,6 +178,9 @@ private:
|
||||
*/
|
||||
Calamares::GeoIP::RegionZonePair m_startingTimezone;
|
||||
|
||||
/// @brief The timezone set in the system when Calamares started (not from config)
|
||||
Calamares::GeoIP::RegionZonePair m_originalTimezone;
|
||||
|
||||
/** @brief Handler for GeoIP lookup (if configured)
|
||||
*
|
||||
* The GeoIP lookup needs to be started at some suitable time,
|
||||
|
||||
@@ -130,6 +130,12 @@ LocaleViewStep::onLeave()
|
||||
m_config->finalizeGlobalStorage();
|
||||
}
|
||||
|
||||
void
|
||||
LocaleViewStep::onCancel()
|
||||
{
|
||||
m_config->cancel();
|
||||
}
|
||||
|
||||
void
|
||||
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
|
||||
void onActivate() override;
|
||||
void onLeave() override;
|
||||
void onCancel() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
|
||||
@@ -83,6 +83,12 @@ LocaleQmlViewStep::onLeave()
|
||||
m_config->finalizeGlobalStorage();
|
||||
}
|
||||
|
||||
void
|
||||
LocaleQmlViewStep::onCancel()
|
||||
{
|
||||
m_config->cancel();
|
||||
}
|
||||
|
||||
void
|
||||
LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
|
||||
@@ -34,8 +34,9 @@ public:
|
||||
bool isAtBeginning() const override;
|
||||
bool isAtEnd() const override;
|
||||
|
||||
virtual void onActivate() override;
|
||||
virtual void onLeave() override;
|
||||
void onActivate() override;
|
||||
void onLeave() override;
|
||||
void onCancel() override;
|
||||
|
||||
Calamares::JobList jobs() const override;
|
||||
|
||||
|
||||
@@ -918,6 +918,8 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
"will use gpt for efi or msdos for bios";
|
||||
}
|
||||
gs->insert( "defaultPartitionTableType", partitionTableName );
|
||||
gs->insert( "createHybridBootloaderLayout",
|
||||
Calamares::getBool( configurationMap, "createHybridBootloaderLayout", false ) );
|
||||
|
||||
// Now that we have the config, we load the PartitionCoreModule in the background
|
||||
// because it could take a while. Then when it's done, we can set up the widgets
|
||||
|
||||
@@ -616,7 +616,7 @@ canonicalFilesystemName( const QString& fsName, FileSystem::Type* fsType )
|
||||
*fsType = FileSystem::Unknown;
|
||||
}
|
||||
#ifdef DEBUG_FILESYSTEMS
|
||||
// This bit is for distro's debugging their settings, and shows
|
||||
// This bit is for distros who are debugging their settings, and shows
|
||||
// all the strings that KPMCore is matching against for FS type.
|
||||
{
|
||||
Logger::CDebug d;
|
||||
|
||||
@@ -89,6 +89,11 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
const bool isEfi = PartUtils::isEfiSystem();
|
||||
|
||||
bool createHybridBootloaderLayout = false;
|
||||
if ( gs->contains( "createHybridBootloaderLayout" ) ) {
|
||||
createHybridBootloaderLayout = gs->value( "createHybridBootloaderLayout" ).toBool();
|
||||
}
|
||||
|
||||
// Partition sizes are expressed in MiB, should be multiples of
|
||||
// the logical sector size (usually 512B). EFI starts with 2MiB
|
||||
@@ -105,7 +110,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
|
||||
PartitionTable::TableType partType = PartitionTable::nameToTableType( o.defaultPartitionTableType );
|
||||
if ( partType == PartitionTable::unknownTableType )
|
||||
{
|
||||
partType = isEfi ? PartitionTable::gpt : PartitionTable::msdos;
|
||||
partType = ( isEfi || createHybridBootloaderLayout ) ? PartitionTable::gpt : PartitionTable::msdos;
|
||||
}
|
||||
// last usable sector possibly allowing for secondary GPT using 66 sectors (256 entries)
|
||||
const qint64 lastUsableSector = dev->totalLogical() - ( partType == PartitionTable::gpt ? 67 : 1 );
|
||||
@@ -118,7 +123,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
|
||||
|
||||
core->createPartitionTable( dev, partType );
|
||||
|
||||
if ( isEfi )
|
||||
if ( createHybridBootloaderLayout || isEfi )
|
||||
{
|
||||
qint64 uefisys_part_sizeB = PartUtils::efiFilesystemRecommendedSize();
|
||||
qint64 efiSectorCount = Calamares::bytesToSectors( uefisys_part_sizeB, dev->logicalSize() );
|
||||
@@ -144,6 +149,25 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
|
||||
}
|
||||
core->createPartition( dev, efiPartition, KPM_PARTITION_FLAG_ESP );
|
||||
firstFreeSector = lastSector + 1;
|
||||
|
||||
if ( createHybridBootloaderLayout )
|
||||
{
|
||||
qint64 bios_part_sizeB = 8_MiB;
|
||||
qint64 biosSectorCount = Calamares::bytesToSectors( bios_part_sizeB, dev->logicalSize() );
|
||||
Q_ASSERT( biosSectorCount > 0 );
|
||||
|
||||
qint64 lastSector = firstFreeSector + biosSectorCount - 1;
|
||||
Partition* biosPartition = KPMHelpers::createNewPartition( dev->partitionTable(),
|
||||
*dev,
|
||||
PartitionRole( PartitionRole::Primary ),
|
||||
FileSystem::Unformatted,
|
||||
QString(),
|
||||
firstFreeSector,
|
||||
lastSector,
|
||||
KPM_PARTITION_FLAG( None ) );
|
||||
core->createPartition( dev, biosPartition, KPM_PARTITION_FLAG( BiosGrub ) );
|
||||
firstFreeSector = lastSector + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const bool mayCreateSwap
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/ColorUtils.h"
|
||||
#include "core/PartitionModel.h"
|
||||
#include "core/SizeUtils.h"
|
||||
#include "core/KPMHelpers.h"
|
||||
|
||||
#include "utils/Gui.h"
|
||||
#include "utils/Logger.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
|
||||
#include <kpmcore/core/device.h>
|
||||
#include <kpmcore/fs/filesystem.h>
|
||||
#include <kpmcore/core/partition.h>
|
||||
|
||||
// Qt
|
||||
#include <QGuiApplication>
|
||||
@@ -41,6 +43,12 @@ buildUnknownDisklabelTexts( Device* dev )
|
||||
return texts;
|
||||
}
|
||||
|
||||
static uint
|
||||
getPartitionModelIndexFlags( const QModelIndex& index )
|
||||
{
|
||||
return static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() )->property( "_calamares_flags" ).toUInt();
|
||||
}
|
||||
|
||||
PartitionLabelsView::PartitionLabelsView( QWidget* parent )
|
||||
: QAbstractItemView( parent )
|
||||
, m_canBeSelected( []( const QModelIndex& ) { return true; } )
|
||||
@@ -190,6 +198,11 @@ PartitionLabelsView::buildTexts( const QModelIndex& index ) const
|
||||
{
|
||||
firstLine = tr( "EFI system", "@label" );
|
||||
}
|
||||
else if ( index.data( PartitionModel::FileSystemTypeRole ).toInt() == FileSystem::Unformatted
|
||||
&& getPartitionModelIndexFlags( index ) & KPM_PARTITION_FLAG( BiosGrub ) )
|
||||
{
|
||||
firstLine = tr("BIOS boot", "@label" );
|
||||
}
|
||||
else if ( index.data( PartitionModel::FileSystemTypeRole ).toInt() == FileSystem::LinuxSwap )
|
||||
{
|
||||
firstLine = tr( "Swap", "@label" );
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#
|
||||
# Both quantities must be at least 32MiB, this is enforced by the EFI
|
||||
# spec. If minimum is not specified, it defaults to the recommended
|
||||
# size. Distro's that allow more user latitude can set the minimum lower.
|
||||
# size. Distros that allow more user latitude can set the minimum lower.
|
||||
efi:
|
||||
mountPoint: "/boot/efi"
|
||||
recommendedSize: 300MiB
|
||||
@@ -178,6 +178,13 @@ initialSwapChoice: none
|
||||
#
|
||||
# defaultPartitionTableType: msdos
|
||||
|
||||
# Specify whether to create a partition table layout suitable for a hybrid
|
||||
# (BIOS + EFI) bootloader installation. This will prepend both bios-boot and
|
||||
# EFI system partitions to the partition layout, regardless of whether the
|
||||
# booted system uses BIOS or EFI firmware. Defaults to false.
|
||||
#
|
||||
# createHybridBootloaderLayout: false
|
||||
|
||||
# Requirement for partition table type
|
||||
#
|
||||
# Restrict the installation on disks that match the type of partition
|
||||
@@ -294,7 +301,7 @@ essentialMounts: [ "live-*", "control", "ventoy" ]
|
||||
|
||||
# When enableLuksAutomatedPartitioning is true, this option will pre-check
|
||||
# encryption checkbox. This option is only usefull to help people to not forget
|
||||
# to cypher their disk when installing in enterprise (for exemple).
|
||||
# to cypher their disk when installing in enterprise (for example).
|
||||
#preCheckEncryption: false
|
||||
|
||||
# LVM support
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
# of the details of that particular kind of tracking. If no policy
|
||||
# is set, that tracking style is disabled. The example policy links
|
||||
# go to Calamares' generic user manual (which is a terrible idea
|
||||
# for distro's: you have GDPR obligations under most of these tracking
|
||||
# styles, so do your homework).
|
||||
# for a distribution: you have GDPR obligations under most of these
|
||||
# tracking styles, so do your homework).
|
||||
#
|
||||
# Each area may have other configuration keys, depending on the
|
||||
# area and how it needs to be configured.
|
||||
|
||||
@@ -1017,6 +1017,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
|
||||
// Renaming of Autologin -> AutoLogin in 4ffa79d4cf also affected
|
||||
// configuration keys, which was not intended. Accept both.
|
||||
m_displayAutoLogin = Calamares::getBool( configurationMap, "displayAutologin", false );
|
||||
m_doAutoLogin = either(
|
||||
Calamares::getBool, configurationMap, QStringLiteral( "doAutologin" ), QStringLiteral( "doAutoLogin" ), false );
|
||||
|
||||
|
||||
@@ -216,6 +216,8 @@ public:
|
||||
/// Write /etc/hosts ?
|
||||
bool writeEtcHosts() const { return m_writeEtcHosts; }
|
||||
|
||||
/// Should the user be able to changed the value of autologin?
|
||||
bool displayAutoLogin() const { return m_displayAutoLogin; }
|
||||
/// Should the user be automatically logged-in?
|
||||
bool doAutoLogin() const { return m_doAutoLogin; }
|
||||
/// Should the root password be written (if false, no password is set and the root account is disabled for login)
|
||||
@@ -343,6 +345,7 @@ private:
|
||||
QString m_rootPassword;
|
||||
QString m_rootPasswordSecondary;
|
||||
|
||||
bool m_displayAutoLogin = false;
|
||||
bool m_doAutoLogin = false;
|
||||
|
||||
bool m_writeRootPassword = true;
|
||||
|
||||
@@ -136,6 +136,7 @@ UsersPage::UsersPage( Config* config, QWidget* parent )
|
||||
connect( config, &Config::loginNameChanged, ui->textBoxLoginName, &QLineEdit::setText );
|
||||
connect( config, &Config::loginNameStatusChanged, this, &UsersPage::reportLoginNameStatus );
|
||||
|
||||
ui->checkBoxDoAutoLogin->setVisible( m_config->displayAutoLogin() );
|
||||
ui->checkBoxDoAutoLogin->setChecked( m_config->doAutoLogin() );
|
||||
connect( ui->checkBoxDoAutoLogin,
|
||||
Calamares::checkBoxStateChangedSignal,
|
||||
|
||||
@@ -101,6 +101,13 @@ doReusePassword: true
|
||||
# or another. Weak(er) passwords may be allowed, may cause a warning,
|
||||
# or may be forbidden entirely.
|
||||
|
||||
# Autologin choice can be display to the user.
|
||||
# Possible values are:
|
||||
# - true to display it
|
||||
# - false to hide it
|
||||
# By default, this value is set to true.
|
||||
displayAutologin: true
|
||||
|
||||
# You can control the initial state for the 'autologin checkbox' here.
|
||||
# Possible values are:
|
||||
# - true to check or
|
||||
|
||||
@@ -31,6 +31,7 @@ properties:
|
||||
sudoersGroup: { type: string }
|
||||
sudoersConfigureWithGroup: { type: boolean, default: false }
|
||||
# Skip login (depends on displaymanager support)
|
||||
displayAutologin: { type: boolean, default: true }
|
||||
doAutologin: { type: boolean, default: true }
|
||||
# Root password separate from user password?
|
||||
setRootPassword: { type: boolean, default: true }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
@@ -40,7 +39,8 @@ requirements:
|
||||
|
||||
# To check for internet connectivity, Calamares does a HTTP GET
|
||||
# on this URL; on success (e.g. HTTP code 200) internet is OK.
|
||||
# Use a privacy-respecting URL here, preferably in your distro's domain.
|
||||
# Use a privacy-respecting URL here, preferably in your own
|
||||
# distribution domain.
|
||||
#
|
||||
# The URL is only used if "internet" is in the *check* list below.
|
||||
internetCheckUrl: http://example.com
|
||||
|
||||
Reference in New Issue
Block a user