diff --git a/src/modules/keyboard/CMakeLists.txt b/src/modules/keyboard/CMakeLists.txt index 99405c99f..c2a67aa22 100644 --- a/src/modules/keyboard/CMakeLists.txt +++ b/src/modules/keyboard/CMakeLists.txt @@ -6,6 +6,7 @@ calamares_add_plugin( keyboard SOURCES KeyboardViewStep.cpp KeyboardPage.cpp + SetKeyboardLayoutJob.cpp keyboardwidget/keyboardglobal.cpp keyboardwidget/keyboardpreview.cpp UI diff --git a/src/modules/keyboard/KeyboardPage.cpp b/src/modules/keyboard/KeyboardPage.cpp index 6961306a0..7170505c5 100644 --- a/src/modules/keyboard/KeyboardPage.cpp +++ b/src/modules/keyboard/KeyboardPage.cpp @@ -24,6 +24,7 @@ #include "ui_KeyboardPage.h" #include "keyboardwidget/keyboardpreview.h" +#include "SetKeyboardLayoutJob.h" #include "GlobalStorage.h" #include "JobQueue.h" @@ -188,6 +189,25 @@ KeyboardPage::prettyStatus() const } +QList< Calamares::job_ptr > +KeyboardPage::createJobs( const QString& xOrgConfFileName, + const QString& convertedKeymapPath ) +{ + QList< Calamares::job_ptr > list; + QString selectedModel = m_models.value( ui->comboBoxModel->currentText(), + "pc105" ); + + Calamares::Job* j = new SetKeyboardLayoutJob( selectedModel, + m_selectedLayout, + m_selectedVariant, + xOrgConfFileName, + convertedKeymapPath ); + list.append( Calamares::job_ptr( j ) ); + + return list; +} + + void KeyboardPage::finalize() { diff --git a/src/modules/keyboard/KeyboardPage.h b/src/modules/keyboard/KeyboardPage.h index 82b965a13..e4ed5b715 100644 --- a/src/modules/keyboard/KeyboardPage.h +++ b/src/modules/keyboard/KeyboardPage.h @@ -25,6 +25,8 @@ #include "keyboardwidget/keyboardglobal.h" +#include "Typedefs.h" + #include #include @@ -46,6 +48,9 @@ public: QString prettyStatus() const; + QList< Calamares::job_ptr > createJobs( const QString& xOrgConfFileName, + const QString& convertedKeymapPath ); + void finalize(); protected slots: @@ -55,7 +60,8 @@ protected slots: QListWidgetItem* previous ); private: - class LayoutItem : public QListWidgetItem { + class LayoutItem : public QListWidgetItem + { public: QString data; KeyboardGlobal::KeyboardInfo info; diff --git a/src/modules/keyboard/KeyboardViewStep.cpp b/src/modules/keyboard/KeyboardViewStep.cpp index ab8129b19..d613171e3 100644 --- a/src/modules/keyboard/KeyboardViewStep.cpp +++ b/src/modules/keyboard/KeyboardViewStep.cpp @@ -18,6 +18,9 @@ #include "KeyboardViewStep.h" +#include "JobQueue.h" +#include "GlobalStorage.h" + #include "KeyboardPage.h" @@ -97,7 +100,7 @@ KeyboardViewStep::isAtEnd() const QList< Calamares::job_ptr > KeyboardViewStep::jobs() const { - return QList< Calamares::job_ptr >(); + return m_jobs; } @@ -105,5 +108,38 @@ void KeyboardViewStep::onLeave() { m_widget->finalize(); + m_jobs = m_widget->createJobs( m_xOrgConfFileName, m_convertedKeymapPath ); m_prettyStatus = m_widget->prettyStatus(); } + + +void +KeyboardViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + // Save the settings to the global settings for the SetKeyboardLayoutJob to use + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + + if ( configurationMap.contains( "xOrgConfFileName" ) && + configurationMap.value( "xOrgConfFileName" ).type() == QVariant::String && + !configurationMap.value( "xOrgConfFileName" ).toString().isEmpty() ) + { + m_xOrgConfFileName = configurationMap.value( "xOrgConfFileName" ) + .toString(); + } + else + { + m_xOrgConfFileName = "00-keyboard.conf"; + } + + if ( configurationMap.contains( "convertedKeymapPath" ) && + configurationMap.value( "convertedKeymapPath" ).type() == QVariant::String && + !configurationMap.value( "convertedKeymapPath" ).toString().isEmpty() ) + { + m_convertedKeymapPath = configurationMap.value( "convertedKeymapPath" ) + .toString(); + } + else + { + m_convertedKeymapPath = QString(); + } +} diff --git a/src/modules/keyboard/KeyboardViewStep.h b/src/modules/keyboard/KeyboardViewStep.h index c5d8328c4..d88343bda 100644 --- a/src/modules/keyboard/KeyboardViewStep.h +++ b/src/modules/keyboard/KeyboardViewStep.h @@ -54,10 +54,17 @@ public: void onLeave() override; + void setConfigurationMap( const QVariantMap& configurationMap ) override; + private: KeyboardPage* m_widget; bool m_nextEnabled; QString m_prettyStatus; + + QString m_xOrgConfFileName; + QString m_convertedKeymapPath; + + QList< Calamares::job_ptr > m_jobs; }; #endif // KEYBOARDVIEWSTEP_H diff --git a/src/modules/keyboard/SetKeyboardLayoutJob.cpp b/src/modules/keyboard/SetKeyboardLayoutJob.cpp new file mode 100644 index 000000000..162b97f54 --- /dev/null +++ b/src/modules/keyboard/SetKeyboardLayoutJob.cpp @@ -0,0 +1,285 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * Copyright 2014, Kevin Kofler + * + * Portions from systemd (localed.c): + * Copyright 2011 Lennart Poettering + * Copyright 2013 Kay Sievers + * (originally under LGPLv2.1+, used under the LGPL to GPL conversion clause) + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include + +#include "JobQueue.h" +#include "GlobalStorage.h" +#include "utils/Logger.h" +#include "utils/CalamaresUtilsSystem.h" + +#include +#include +#include +#include +#include + + +SetKeyboardLayoutJob::SetKeyboardLayoutJob( const QString& model, + const QString& layout, + const QString& variant, + const QString& xOrgConfFileName, + const QString& convertedKeymapPath ) + : Calamares::Job() + , m_model( model ) + , m_layout( layout ) + , m_variant( variant ) + , m_xOrgConfFileName( xOrgConfFileName ) + , m_convertedKeymapPath( convertedKeymapPath ) +{ +} + + +QString +SetKeyboardLayoutJob::prettyName() const +{ + return tr( "Set keyboard model to %1, layout to %2-%3" ).arg( m_model ) + .arg( m_layout ) + .arg( m_variant ); +} + + +QString +SetKeyboardLayoutJob::findConvertedKeymap( const QString& convertedKeymapPath ) const +{ + // No search path supplied, assume the distribution does not provide + // converted keymaps + if ( convertedKeymapPath.isEmpty() ) + return QString(); + + QDir convertedKeymapDir( convertedKeymapPath ); + QString name = m_variant.isEmpty() ? m_layout : ( m_layout + '-' + m_variant ); + + if ( convertedKeymapDir.exists( name + ".map" ) + || convertedKeymapDir.exists( name + ".map.gz" ) ) + { + cDebug() << "Found converted keymap" << name; + + return name; + } + + return QString(); +} + + +QString +SetKeyboardLayoutJob::findLegacyKeymap() const +{ + int bestMatching = 0; + QString name; + + QFile file( ":/kbd-model-map" ); + file.open( QIODevice::ReadOnly | QIODevice::Text ); + QTextStream stream( &file ); + while ( !stream.atEnd() ) + { + QString line = stream.readLine().trimmed(); + if ( line.isEmpty() || line.startsWith( '#' ) ) + continue; + + QStringList mapping = line.split( '\t', QString::SkipEmptyParts ); + if ( mapping.size() < 5 ) + continue; + + int matching = 0; + + // Determine how well matching this entry is + // We assume here that we have one X11 layout. If the UI changes to + // allow more than one layout, this should change too. + if ( m_layout == mapping[1] ) + // If we got an exact match, this is best + matching = 10; + // Look for an entry whose first layout matches ours + else if ( mapping[1].startsWith( m_layout + ',' ) ) + matching = 5; + + if ( matching > 0 ) + { + if ( m_model.isEmpty() || m_model == mapping[2] ) + matching++; + + QString mappingVariant = mapping[3]; + if ( mappingVariant == "-" ) + mappingVariant = QString(); + else if ( mappingVariant.startsWith( ',' ) ) + mappingVariant.remove( 1, 0 ); + + if ( m_variant == mappingVariant ) + matching++; + + // We ignore mapping[4], the xkb options, for now. If we ever + // allow setting options in the UI, we should match them here. + } + + // The best matching entry so far, then let's save that + if ( matching >= qMax( bestMatching, 1 ) ) + { + cDebug() << "Found legacy keymap" << mapping[0] + << "with score" << matching; + + if ( matching > bestMatching ) + { + bestMatching = matching; + name = mapping[0]; + } + } + } + + return name; +} + + +bool +SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, + const QString& convertedKeymapPath ) const +{ + QString keymap = findConvertedKeymap( convertedKeymapPath ); + if ( keymap.isEmpty() ) + keymap = findLegacyKeymap(); + if ( keymap.isEmpty() ) + { + cDebug() << "Trying to use X11 layout" << m_layout + << "as the virtual console layout"; + keymap = m_layout; + } + + QStringList existingLines; + + // Read in the existing vconsole.conf, if it exists + QFile file( vconsoleConfPath ); + if ( file.exists() ) + { + file.open( QIODevice::ReadOnly | QIODevice::Text ); + QTextStream stream( &file ); + while ( !stream.atEnd() ) + existingLines << stream.readLine(); + file.close(); + if ( stream.status() != QTextStream::Ok ) + return false; + } + + // Write out the existing lines and replace the KEYMAP= line + file.open( QIODevice::WriteOnly | QIODevice::Text ); + QTextStream stream( &file ); + bool found = false; + foreach ( const QString& existingLine, existingLines ) + { + if ( existingLine.trimmed().startsWith( "KEYMAP=" ) ) + { + stream << "KEYMAP=" << keymap << '\n'; + found = true; + } + else + stream << existingLine << '\n'; + } + // Add a KEYMAP= line if there wasn't any + if ( !found ) + stream << "KEYMAP=" << keymap << '\n'; + stream.flush(); + file.close(); + + return ( stream.status() == QTextStream::Ok ); +} + + +bool +SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const +{ + QFile file( keyboardConfPath ); + file.open( QIODevice::WriteOnly | QIODevice::Text ); + QTextStream stream( &file ); + + stream << "# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" + "# manually too freely.\n" + "Section \"InputClass\"\n" + " Identifier \"system-keyboard\"\n" + " MatchIsKeyboard \"on\"\n"; + + if ( !m_layout.isEmpty() ) + stream << " Option \"XkbLayout\" \"" << m_layout << "\"\n"; + + if ( !m_model.isEmpty() ) + stream << " Option \"XkbModel\" \"" << m_model << "\"\n"; + + if ( !m_variant.isEmpty() ) + stream << " Option \"XkbVariant\" \"" << m_variant << "\"\n"; + + stream << "EndSection\n"; + stream.flush(); + + file.close(); + + return ( stream.status() == QTextStream::Ok ); +} + + +Calamares::JobResult +SetKeyboardLayoutJob::exec() +{ + // Read the location of the destination's / in the host file system from + // the global settings + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + QDir destDir( gs->value( "rootMountPoint" ).toString() ); + + // Get the path to the destination's /etc/vconsole.conf + QString vconsoleConfPath = destDir.absoluteFilePath( "etc/vconsole.conf" ); + + // Get the path to the destination's /etc/X11/xorg.conf.d/00-keyboard.conf + QString xorgConfDPath; + QString keyboardConfPath; + if ( QDir::isAbsolutePath( m_xOrgConfFileName ) ) + { + keyboardConfPath = m_xOrgConfFileName; + while ( keyboardConfPath.startsWith( '/' ) ) + keyboardConfPath.remove( 0, 1 ); + keyboardConfPath = destDir.absoluteFilePath( keyboardConfPath ); + xorgConfDPath = QFileInfo( keyboardConfPath ).path(); + } + else + { + xorgConfDPath = destDir.absoluteFilePath( "etc/X11/xorg.conf.d" ); + keyboardConfPath = QDir( xorgConfDPath ) + .absoluteFilePath( m_xOrgConfFileName ); + } + destDir.mkpath( xorgConfDPath ); + + // Get the path to the destination's path to the converted key mappings + QString convertedKeymapPath = m_convertedKeymapPath; + if ( !convertedKeymapPath.isEmpty() ) + { + while ( convertedKeymapPath.startsWith( '/' ) ) + convertedKeymapPath.remove( 0, 1 ); + convertedKeymapPath = destDir.absoluteFilePath( convertedKeymapPath ); + } + + if ( !writeVConsoleData( vconsoleConfPath, convertedKeymapPath ) ) + return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for the virtual console." ), + tr( "Failed to write to %1" ).arg( vconsoleConfPath ) ); + + if ( !writeX11Data( keyboardConfPath ) ) + return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for X11." ), + tr( "Failed to write to %1" ).arg( keyboardConfPath ) ); + + return Calamares::JobResult::ok(); +} diff --git a/src/modules/keyboard/SetKeyboardLayoutJob.h b/src/modules/keyboard/SetKeyboardLayoutJob.h new file mode 100644 index 000000000..096a1fce3 --- /dev/null +++ b/src/modules/keyboard/SetKeyboardLayoutJob.h @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * Copyright 2014, Kevin Kofler + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef SETKEYBOARDLAYOUTJOB_H +#define SETKEYBOARDLAYOUTJOB_H + +#include + + +class SetKeyboardLayoutJob : public Calamares::Job +{ + Q_OBJECT +public: + SetKeyboardLayoutJob( const QString& model, + const QString& layout, + const QString& variant, + const QString& xOrgConfFileName, + const QString& convertedKeymapPath ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + +private: + QString findConvertedKeymap( const QString& convertedKeymapPath ) const; + QString findLegacyKeymap() const; + bool writeVConsoleData( const QString& vconsoleConfPath, + const QString& convertedKeymapPath ) const; + bool writeX11Data( const QString& keyboardConfPath ) const; + + QString m_model; + QString m_layout; + QString m_variant; + QString m_xOrgConfFileName; + QString m_convertedKeymapPath; +}; + +#endif /* SETKEYBOARDLAYOUTJOB_H */ diff --git a/src/modules/keyboard/kbd-model-map b/src/modules/keyboard/kbd-model-map new file mode 100644 index 000000000..7f76f8af8 --- /dev/null +++ b/src/modules/keyboard/kbd-model-map @@ -0,0 +1,67 @@ +# Copied from systemd-localed +# http://cgit.freedesktop.org/systemd/systemd/log/src/locale/kbd-model-map +# (originally under LGPLv2.1+, used under the LGPL to GPL conversion clause) +# Generated from system-config-keyboard's model list +# consolelayout xlayout xmodel xvariant xoptions +sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +nl nl pc105 - terminate:ctrl_alt_bksp +mk-utf mk,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +trq tr pc105 - terminate:ctrl_alt_bksp +uk gb pc105 - terminate:ctrl_alt_bksp +is-latin1 is pc105 - terminate:ctrl_alt_bksp +de de pc105 - terminate:ctrl_alt_bksp +la-latin1 latam pc105 - terminate:ctrl_alt_bksp +us us pc105+inet - terminate:ctrl_alt_bksp +ko kr pc105 - terminate:ctrl_alt_bksp +ro-std ro pc105 std terminate:ctrl_alt_bksp +de-latin1 de pc105 - terminate:ctrl_alt_bksp +slovene si pc105 - terminate:ctrl_alt_bksp +hu101 hu pc105 qwerty terminate:ctrl_alt_bksp +jp106 jp jp106 - terminate:ctrl_alt_bksp +croat hr pc105 - terminate:ctrl_alt_bksp +it2 it pc105 - terminate:ctrl_alt_bksp +hu hu pc105 - terminate:ctrl_alt_bksp +sr-latin rs pc105 latin terminate:ctrl_alt_bksp +fi fi pc105 - terminate:ctrl_alt_bksp +fr_CH ch pc105 fr terminate:ctrl_alt_bksp +dk-latin1 dk pc105 - terminate:ctrl_alt_bksp +fr fr pc105 - terminate:ctrl_alt_bksp +it it pc105 - terminate:ctrl_alt_bksp +ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin1 fr pc105 - terminate:ctrl_alt_bksp +sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +be-latin1 be pc105 - terminate:ctrl_alt_bksp +dk dk pc105 - terminate:ctrl_alt_bksp +fr-pc fr pc105 - terminate:ctrl_alt_bksp +bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +it-ibm it pc105 - terminate:ctrl_alt_bksp +cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +br-abnt2 br abnt2 - terminate:ctrl_alt_bksp +ro ro pc105 - terminate:ctrl_alt_bksp +us-acentos us pc105 intl terminate:ctrl_alt_bksp +pt-latin1 pt pc105 - terminate:ctrl_alt_bksp +ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp +tj_alt-UTF8 tj pc105 - terminate:ctrl_alt_bksp +de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp +no no pc105 - terminate:ctrl_alt_bksp +bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +dvorak us pc105 dvorak terminate:ctrl_alt_bksp +dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp +ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp +pl2 pl pc105 - terminate:ctrl_alt_bksp +es es pc105 - terminate:ctrl_alt_bksp +ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp +ie ie pc105 - terminate:ctrl_alt_bksp +et ee pc105 - terminate:ctrl_alt_bksp +sk-qwerty sk pc105 - terminate:ctrl_alt_bksp,qwerty +fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp +fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp +cf ca pc105 - terminate:ctrl_alt_bksp +sv-latin1 se pc105 - terminate:ctrl_alt_bksp +sr-cy rs pc105 - terminate:ctrl_alt_bksp +gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +by by,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +il il pc105 - terminate:ctrl_alt_bksp +kazakh kz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +lt lt pc105 - terminate:ctrl_alt_bksp diff --git a/src/modules/keyboard/keyboard.conf b/src/modules/keyboard/keyboard.conf new file mode 100644 index 000000000..a2606b94e --- /dev/null +++ b/src/modules/keyboard/keyboard.conf @@ -0,0 +1,8 @@ +--- +# The name of the file to write X11 keyboard settings to +# The default value is the name used by upstream systemd-localed. +# Relative paths are assumed to be relative to /etc/X11/xorg.conf.d +xOrgConfFileName: "/etc/X11/xorg.conf.d/00-keyboard.conf" +# The path to search for keymaps converted from X11 to kbd format +# Leave this empty if the setting does not make sense on your distribution. +convertedKeymapPath: "/lib/kbd/keymaps/xkb" diff --git a/src/modules/keyboard/keyboard.qrc b/src/modules/keyboard/keyboard.qrc index 3d5b1c1cd..dd211e630 100644 --- a/src/modules/keyboard/keyboard.qrc +++ b/src/modules/keyboard/keyboard.qrc @@ -1,5 +1,6 @@ + kbd-model-map images/restore.png