diff --git a/src/modules/keyboard/AdditionalLayoutInfo.h b/src/modules/keyboard/AdditionalLayoutInfo.h new file mode 100644 index 000000000..660449952 --- /dev/null +++ b/src/modules/keyboard/AdditionalLayoutInfo.h @@ -0,0 +1,24 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Artem Grinev + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef KEYBOARD_ADDITIONAL_LAYOUT_INFO_H +#define KEYBOARD_ADDITIONAL_LAYOUT_INFO_H + +#include + +struct AdditionalLayoutInfo { + QString additionalLayout; + QString additionalVariant; + + QString groupSwitcher; + + QString vconsoleKeymap; +}; + +#endif diff --git a/src/modules/keyboard/Config.cpp b/src/modules/keyboard/Config.cpp index 7f7eb7b27..b3365cff6 100644 --- a/src/modules/keyboard/Config.cpp +++ b/src/modules/keyboard/Config.cpp @@ -49,6 +49,100 @@ xkbmap_layout_args( const QString& layout, const QString& variant ) return r; } +static inline QStringList +xkbmap_layout_args( const QStringList& layouts, const QStringList& variants, const QString& switchOption = "grp:alt_shift_toggle") +{ + if ( layouts.size() != variants.size() ) + { + cError() << "Number of layouts and variants must be equal (empty string should be used if there is no corresponding variant)"; + return QStringList(); + } + + QStringList r{ "-layout", layouts.join( "," ) }; + + if ( !variants.isEmpty() ) + { + r << "-variant" << variants.join( "," ); + } + + if ( !switchOption.isEmpty()) + { + r << "-option" << switchOption; + } + + return r; +} + +/* Returns group-switch setxkbd option if set + * or an empty string otherwise + */ +static inline QString +xkbmap_query_grp_option() +{ + QProcess setxkbmapQuery; + setxkbmapQuery.start( "setxkbmap", { "-query" } ); + setxkbmapQuery.waitForFinished(); + + QString outputLine; + + do + { + outputLine = setxkbmapQuery.readLine(); + } + while( setxkbmapQuery.canReadLine() && !outputLine.startsWith("options:") ); + + if( !outputLine.startsWith( "options:" ) ) + { + return QString(); + } + + int index = outputLine.indexOf( "grp:" ); + + if( index == -1 ) + { + return QString(); + } + + //it's either in the end of line or before the other option so \s or , + int lastIndex = outputLine.indexOf( QRegExp( "[\\s,]" ), index ); + + return outputLine.mid( index, lastIndex-1 ); +} + +AdditionalLayoutInfo Config::getAdditionalLayoutInfo( const QString &layout ) +{ + QFile layoutTable( ":/non-ascii-layouts" ); + + if( !layoutTable.open( QIODevice::ReadOnly | QIODevice::Text ) ) { + cError() << "Non-ASCII layout table could not be opened"; + return AdditionalLayoutInfo(); + } + + QString tableLine; + + do + { + tableLine = layoutTable.readLine(); + } + while( layoutTable.canReadLine() && !tableLine.startsWith( layout ) ); + + if( !tableLine.startsWith( layout ) ) + { + return AdditionalLayoutInfo(); + } + + QStringList tableEntries = tableLine.split( " ", SplitSkipEmptyParts ); + + AdditionalLayoutInfo r; + + r.additionalLayout = tableEntries[1]; + r.additionalVariant = tableEntries[2] == "-" ? "" : tableEntries[2]; + + r.vconsoleKeymap = tableEntries[3]; + + return r; +} + Config::Config( QObject* parent ) : QObject( parent ) , m_keyboardModelsModel( new KeyboardModelsModel( this ) ) @@ -82,8 +176,35 @@ Config::Config( QObject* parent ) } connect( &m_setxkbmapTimer, &QTimer::timeout, this, [=] { - QProcess::execute( "setxkbmap", xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) ); - cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant; + m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout ); + + if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() ) + { + m_additionalLayoutInfo.groupSwitcher = xkbmap_query_grp_option(); + + if( m_additionalLayoutInfo.groupSwitcher.isEmpty() ) + { + m_additionalLayoutInfo.groupSwitcher = "grp:alt_shift_toggle"; + } + + QProcess::execute( "setxkbmap", xkbmap_layout_args( + { m_additionalLayoutInfo.additionalLayout, m_selectedLayout }, + { m_additionalLayoutInfo.additionalVariant, m_selectedVariant }, + m_additionalLayoutInfo.groupSwitcher ) + ); + + + + cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant + << "(added " << m_additionalLayoutInfo.additionalLayout << "-" + << m_additionalLayoutInfo.additionalVariant << " since current layout is not ASCII-capable)"; + + } + else + { + QProcess::execute( "setxkbmap", xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) ); + cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant; + } m_setxkbmapTimer.disconnect( this ); } ); m_setxkbmapTimer.start( QApplication::keyboardInputInterval() ); @@ -222,6 +343,7 @@ Config::createJobs() Calamares::Job* j = new SetKeyboardLayoutJob( m_selectedModel, m_selectedLayout, m_selectedVariant, + m_additionalLayoutInfo, m_xOrgConfFileName, m_convertedKeymapPath, m_writeEtcDefaultKeyboard ); @@ -372,6 +494,13 @@ Config::finalize() { gs->insert( "keyboardLayout", m_selectedLayout ); gs->insert( "keyboardVariant", m_selectedVariant ); //empty means default variant + + if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() ) + { + gs->insert( "keyboardAdditionalLayout", m_additionalLayoutInfo.additionalLayout); + gs->insert( "keyboardAdditionalLayout", m_additionalLayoutInfo.additionalVariant); + gs->insert( "keyboardVConsoleKeymap", m_additionalLayoutInfo.vconsoleKeymap ); + } } //FIXME: also store keyboard model for something? diff --git a/src/modules/keyboard/Config.h b/src/modules/keyboard/Config.h index 9a3a3a013..031cf8922 100644 --- a/src/modules/keyboard/Config.h +++ b/src/modules/keyboard/Config.h @@ -13,6 +13,7 @@ #include "Job.h" #include "KeyboardLayoutModel.h" +#include "AdditionalLayoutInfo.h" #include #include @@ -41,6 +42,8 @@ public: void setConfigurationMap( const QVariantMap& configurationMap ); + static AdditionalLayoutInfo getAdditionalLayoutInfo( const QString& layout ); + private: void guessLayout( const QStringList& langParts ); void updateVariants( const QPersistentModelIndex& currentItem, QString currentVariant = QString() ); @@ -52,6 +55,10 @@ private: QString m_selectedLayout; QString m_selectedModel; QString m_selectedVariant; + + // Layout (and corresponding info) added if current one doesn't support ASCII (e.g. Russian or Japanese) + AdditionalLayoutInfo m_additionalLayoutInfo; + QTimer m_setxkbmapTimer; // From configuration diff --git a/src/modules/keyboard/KeyboardPage.cpp b/src/modules/keyboard/KeyboardPage.cpp index 07c5cf763..84958761b 100644 --- a/src/modules/keyboard/KeyboardPage.cpp +++ b/src/modules/keyboard/KeyboardPage.cpp @@ -222,6 +222,7 @@ KeyboardPage::createJobs( const QString& xOrgConfFileName, Calamares::Job* j = new SetKeyboardLayoutJob( selectedModel, m_selectedLayout, m_selectedVariant, + AdditionalLayoutInfo(), xOrgConfFileName, convertedKeymapPath, writeEtcDefaultKeyboard ); diff --git a/src/modules/keyboard/SetKeyboardLayoutJob.cpp b/src/modules/keyboard/SetKeyboardLayoutJob.cpp index cabe0b5c0..766044179 100644 --- a/src/modules/keyboard/SetKeyboardLayoutJob.cpp +++ b/src/modules/keyboard/SetKeyboardLayoutJob.cpp @@ -30,9 +30,10 @@ #include -SetKeyboardLayoutJob::SetKeyboardLayoutJob( const QString& model, +SetKeyboardLayoutJob::SetKeyboardLayoutJob(const QString& model, const QString& layout, const QString& variant, + const AdditionalLayoutInfo& additionalLayoutInfo, const QString& xOrgConfFileName, const QString& convertedKeymapPath, bool writeEtcDefaultKeyboard ) @@ -40,6 +41,7 @@ SetKeyboardLayoutJob::SetKeyboardLayoutJob( const QString& model, , m_model( model ) , m_layout( layout ) , m_variant( variant ) + , m_additionalLayoutInfo( additionalLayoutInfo ) , m_xOrgConfFileName( xOrgConfFileName ) , m_convertedKeymapPath( convertedKeymapPath ) , m_writeEtcDefaultKeyboard( writeEtcDefaultKeyboard ) @@ -250,19 +252,32 @@ SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n"; - if ( !m_layout.isEmpty() ) - { - stream << " Option \"XkbLayout\" \"" << m_layout << "\"\n"; - } - if ( !m_model.isEmpty() ) + if ( m_additionalLayoutInfo.additionalLayout.isEmpty() ) { - stream << " Option \"XkbModel\" \"" << m_model << "\"\n"; - } + if ( !m_layout.isEmpty() ) + { + stream << " Option \"XkbLayout\" \"" << m_layout << "\"\n"; + } - if ( !m_variant.isEmpty() ) + if ( !m_variant.isEmpty() ) + { + stream << " Option \"XkbVariant\" \"" << m_variant << "\"\n"; + } + } + else { - stream << " Option \"XkbVariant\" \"" << m_variant << "\"\n"; + if ( !m_layout.isEmpty() ) + { + stream << " Option \"XkbLayout\" \"" << m_additionalLayoutInfo.additionalLayout << "," << m_layout << "\"\n"; + } + + if ( !m_variant.isEmpty() ) + { + stream << " Option \"XkbVariant\" \"" << m_additionalLayoutInfo.additionalVariant << "," << m_variant << "\"\n"; + } + + stream << " Option \"XkbOptions\" \"" << m_additionalLayoutInfo.groupSwitcher << "\"\n"; } stream << "EndSection\n"; diff --git a/src/modules/keyboard/SetKeyboardLayoutJob.h b/src/modules/keyboard/SetKeyboardLayoutJob.h index f1eabe195..b1ce0bb15 100644 --- a/src/modules/keyboard/SetKeyboardLayoutJob.h +++ b/src/modules/keyboard/SetKeyboardLayoutJob.h @@ -12,6 +12,8 @@ #define SETKEYBOARDLAYOUTJOB_H #include "Job.h" +#include "AdditionalLayoutInfo.h" + class SetKeyboardLayoutJob : public Calamares::Job @@ -21,6 +23,7 @@ public: SetKeyboardLayoutJob( const QString& model, const QString& layout, const QString& variant, + const AdditionalLayoutInfo& additionaLayoutInfo, const QString& xOrgConfFileName, const QString& convertedKeymapPath, bool writeEtcDefaultKeyboard ); @@ -38,6 +41,7 @@ private: QString m_model; QString m_layout; QString m_variant; + AdditionalLayoutInfo m_additionalLayoutInfo; QString m_xOrgConfFileName; QString m_convertedKeymapPath; const bool m_writeEtcDefaultKeyboard; diff --git a/src/modules/keyboard/keyboard.qrc b/src/modules/keyboard/keyboard.qrc index dd211e630..4283d8190 100644 --- a/src/modules/keyboard/keyboard.qrc +++ b/src/modules/keyboard/keyboard.qrc @@ -2,5 +2,6 @@ kbd-model-map images/restore.png + non-ascii-layouts diff --git a/src/modules/keyboard/non-ascii-layouts b/src/modules/keyboard/non-ascii-layouts new file mode 100644 index 000000000..83935c190 --- /dev/null +++ b/src/modules/keyboard/non-ascii-layouts @@ -0,0 +1,4 @@ +# Layouts stored here need additional layout (usually us) to provide ASCII support for user + +#layout additional-layout additional-variant vconsole-keymap +ru us - ruwin_alt_sh-UTF-8 diff --git a/src/modules/keyboardq/keyboardq.qrc b/src/modules/keyboardq/keyboardq.qrc index 492f6e213..d2473a8ec 100644 --- a/src/modules/keyboardq/keyboardq.qrc +++ b/src/modules/keyboardq/keyboardq.qrc @@ -3,5 +3,6 @@ ../keyboard/kbd-model-map ../keyboard/images/restore.png keyboardq.qml + ../keyboard/non-ascii-layouts