source: calamares/trunk/fuentes/src/modules/keyboard/SetKeyboardLayoutJob.cpp @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 10.7 KB
Line 
1/* === This file is part of Calamares - <https://github.com/calamares> ===
2 *
3 *   Copyright 2014-2016, Teo Mrnjavac <teo@kde.org>
4 *   Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
5 *
6 *   Portions from systemd (localed.c):
7 *   Copyright 2011 Lennart Poettering
8 *   Copyright 2013 Kay Sievers
9 *   (originally under LGPLv2.1+, used under the LGPL to GPL conversion clause)
10 *
11 *   Calamares is free software: you can redistribute it and/or modify
12 *   it under the terms of the GNU General Public License as published by
13 *   the Free Software Foundation, either version 3 of the License, or
14 *   (at your option) any later version.
15 *
16 *   Calamares is distributed in the hope that it will be useful,
17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 *   GNU General Public License for more details.
20 *
21 *   You should have received a copy of the GNU General Public License
22 *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25#include <SetKeyboardLayoutJob.h>
26
27#include "JobQueue.h"
28#include "GlobalStorage.h"
29#include "utils/Logger.h"
30#include "utils/CalamaresUtilsSystem.h"
31
32#include <QDir>
33#include <QFileInfo>
34#include <QFile>
35#include <QTextStream>
36#include <QSettings>
37
38
39SetKeyboardLayoutJob::SetKeyboardLayoutJob( const QString& model,
40        const QString& layout,
41        const QString& variant,
42        const QString& xOrgConfFileName,
43        const QString& convertedKeymapPath,
44        bool writeEtcDefaultKeyboard )
45    : Calamares::Job()
46    , m_model( model )
47    , m_layout( layout )
48    , m_variant( variant )
49    , m_xOrgConfFileName( xOrgConfFileName )
50    , m_convertedKeymapPath( convertedKeymapPath )
51    , m_writeEtcDefaultKeyboard( writeEtcDefaultKeyboard )
52{
53}
54
55
56QString
57SetKeyboardLayoutJob::prettyName() const
58{
59    return tr( "Set keyboard model to %1, layout to %2-%3" ).arg( m_model )
60           .arg( m_layout )
61           .arg( m_variant );
62}
63
64
65QString
66SetKeyboardLayoutJob::findConvertedKeymap( const QString& convertedKeymapPath ) const
67{
68    // No search path supplied, assume the distribution does not provide
69    // converted keymaps
70    if ( convertedKeymapPath.isEmpty() )
71        return QString();
72
73    QDir convertedKeymapDir( convertedKeymapPath );
74    QString name = m_variant.isEmpty() ? m_layout : ( m_layout + '-' + m_variant );
75
76    if ( convertedKeymapDir.exists( name + ".map" )
77            || convertedKeymapDir.exists( name + ".map.gz" ) )
78    {
79        cDebug() << "Found converted keymap" << name;
80
81        return name;
82    }
83
84    return QString();
85}
86
87
88QString
89SetKeyboardLayoutJob::findLegacyKeymap() const
90{
91    int bestMatching = 0;
92    QString name;
93
94    QFile file( ":/kbd-model-map" );
95    file.open( QIODevice::ReadOnly | QIODevice::Text );
96    QTextStream stream( &file );
97    while ( !stream.atEnd() )
98    {
99        QString line = stream.readLine().trimmed();
100        if ( line.isEmpty() || line.startsWith( '#' ) )
101            continue;
102
103        QStringList mapping = line.split( '\t', QString::SkipEmptyParts );
104        if ( mapping.size() < 5 )
105            continue;
106
107        int matching = 0;
108
109        // Determine how well matching this entry is
110        // We assume here that we have one X11 layout. If the UI changes to
111        // allow more than one layout, this should change too.
112        if ( m_layout == mapping[1] )
113            // If we got an exact match, this is best
114            matching = 10;
115        // Look for an entry whose first layout matches ours
116        else if ( mapping[1].startsWith( m_layout + ',' ) )
117            matching = 5;
118
119        if ( matching > 0 )
120        {
121            if ( m_model.isEmpty() || m_model == mapping[2] )
122                matching++;
123
124            QString mappingVariant = mapping[3];
125            if ( mappingVariant == "-" )
126                mappingVariant = QString();
127            else if ( mappingVariant.startsWith( ',' ) )
128                mappingVariant.remove( 1, 0 );
129
130            if ( m_variant == mappingVariant )
131                matching++;
132
133            // We ignore mapping[4], the xkb options, for now. If we ever
134            // allow setting options in the UI, we should match them here.
135        }
136
137        // The best matching entry so far, then let's save that
138        if ( matching >= qMax( bestMatching, 1 ) )
139        {
140            cDebug() << "Found legacy keymap" << mapping[0]
141                     << "with score" << matching;
142
143            if ( matching > bestMatching )
144            {
145                bestMatching = matching;
146                name = mapping[0];
147            }
148        }
149    }
150
151    return name;
152}
153
154
155bool
156SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath,
157        const QString& convertedKeymapPath ) const
158{
159    QString keymap = findConvertedKeymap( convertedKeymapPath );
160    if ( keymap.isEmpty() )
161        keymap = findLegacyKeymap();
162    if ( keymap.isEmpty() )
163    {
164        cDebug() << "Trying to use X11 layout" << m_layout
165                 << "as the virtual console layout";
166        keymap = m_layout;
167    }
168
169    QStringList existingLines;
170
171    // Read in the existing vconsole.conf, if it exists
172    QFile file( vconsoleConfPath );
173    if ( file.exists() )
174    {
175        file.open( QIODevice::ReadOnly | QIODevice::Text );
176        QTextStream stream( &file );
177        while ( !stream.atEnd() )
178            existingLines << stream.readLine();
179        file.close();
180        if ( stream.status() != QTextStream::Ok )
181            return false;
182    }
183
184    // Write out the existing lines and replace the KEYMAP= line
185    file.open( QIODevice::WriteOnly | QIODevice::Text );
186    QTextStream stream( &file );
187    bool found = false;
188    foreach ( const QString& existingLine, existingLines )
189    {
190        if ( existingLine.trimmed().startsWith( "KEYMAP=" ) )
191        {
192            stream << "KEYMAP=" << keymap << '\n';
193            found = true;
194        }
195        else
196            stream << existingLine << '\n';
197    }
198    // Add a KEYMAP= line if there wasn't any
199    if ( !found )
200        stream << "KEYMAP=" << keymap << '\n';
201    stream.flush();
202    file.close();
203
204    cDebug() << "Written KEYMAP=" << keymap << "to vconsole.conf";
205
206    return ( stream.status() == QTextStream::Ok );
207}
208
209
210bool
211SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
212{
213    QFile file( keyboardConfPath );
214    file.open( QIODevice::WriteOnly | QIODevice::Text );
215    QTextStream stream( &file );
216
217    stream << "# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
218           "# manually too freely.\n"
219           "Section \"InputClass\"\n"
220           "        Identifier \"system-keyboard\"\n"
221           "        MatchIsKeyboard \"on\"\n";
222
223    if ( !m_layout.isEmpty() )
224        stream << "        Option \"XkbLayout\" \"" << m_layout << "\"\n";
225
226    if ( !m_model.isEmpty() )
227        stream << "        Option \"XkbModel\" \"" << m_model << "\"\n";
228
229    if ( !m_variant.isEmpty() )
230        stream << "        Option \"XkbVariant\" \"" << m_variant << "\"\n";
231
232    stream << "EndSection\n";
233    stream.flush();
234
235    file.close();
236
237    cDebug() << "Written XkbLayout" << m_layout <<
238             "; XkbModel" << m_model <<
239             "; XkbVariant" << m_variant << "to X.org file" << keyboardConfPath;
240
241    return ( stream.status() == QTextStream::Ok );
242}
243
244
245bool
246SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPath ) const
247{
248    QFile file( defaultKeyboardPath );
249    file.open( QIODevice::WriteOnly | QIODevice::Text );
250    QTextStream stream( &file );
251
252    stream << "# KEYBOARD CONFIGURATION FILE\n\n"
253           "# Consult the keyboard(5) manual page.\n\n";
254
255    stream << "XKBMODEL=\"" << m_model << "\"\n";
256    stream << "XKBLAYOUT=\"" << m_layout << "\"\n";
257    stream << "XKBVARIANT=\"" << m_variant << "\"\n";
258    stream << "XKBOPTIONS=\"\"\n\n";
259    stream << "BACKSPACE=\"guess\"\n";
260    stream.flush();
261
262    file.close();
263
264    cDebug() << "Written XKBMODEL" << m_model <<
265             "; XKBLAYOUT" << m_layout <<
266             "; XKBVARIANT" << m_variant <<
267             "to /etc/default/keyboard file" << defaultKeyboardPath;
268
269    return ( stream.status() == QTextStream::Ok );
270}
271
272
273Calamares::JobResult
274SetKeyboardLayoutJob::exec()
275{
276    cDebug() << "Executing SetKeyboardLayoutJob";
277    // Read the location of the destination's / in the host file system from
278    // the global settings
279    Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
280    QDir destDir( gs->value( "rootMountPoint" ).toString() );
281
282    // Get the path to the destination's /etc/vconsole.conf
283    QString vconsoleConfPath = destDir.absoluteFilePath( "etc/vconsole.conf" );
284
285    // Get the path to the destination's /etc/X11/xorg.conf.d/00-keyboard.conf
286    QString xorgConfDPath;
287    QString keyboardConfPath;
288    if ( QDir::isAbsolutePath( m_xOrgConfFileName ) )
289    {
290        keyboardConfPath = m_xOrgConfFileName;
291        while ( keyboardConfPath.startsWith( '/' ) )
292            keyboardConfPath.remove( 0, 1 );
293        keyboardConfPath = destDir.absoluteFilePath( keyboardConfPath );
294        xorgConfDPath = QFileInfo( keyboardConfPath ).path();
295    }
296    else
297    {
298        xorgConfDPath = destDir.absoluteFilePath( "etc/X11/xorg.conf.d" );
299        keyboardConfPath = QDir( xorgConfDPath )
300                           .absoluteFilePath( m_xOrgConfFileName );
301    }
302    destDir.mkpath( xorgConfDPath );
303
304    QString defaultKeyboardPath;
305    if ( QDir( destDir.absoluteFilePath( "etc/default" ) ).exists() )
306        defaultKeyboardPath = destDir.absoluteFilePath( "etc/default/keyboard" );
307
308    // Get the path to the destination's path to the converted key mappings
309    QString convertedKeymapPath = m_convertedKeymapPath;
310    if ( !convertedKeymapPath.isEmpty() )
311    {
312        while ( convertedKeymapPath.startsWith( '/' ) )
313            convertedKeymapPath.remove( 0, 1 );
314        convertedKeymapPath = destDir.absoluteFilePath( convertedKeymapPath );
315    }
316
317    if ( !writeVConsoleData( vconsoleConfPath, convertedKeymapPath ) )
318        return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for the virtual console." ),
319                                            tr( "Failed to write to %1" ).arg( vconsoleConfPath ) );
320
321    if ( !writeX11Data( keyboardConfPath ) )
322        return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for X11." ),
323                                            tr( "Failed to write to %1" ).arg( keyboardConfPath ) );
324
325    if ( !defaultKeyboardPath.isEmpty() && m_writeEtcDefaultKeyboard )
326    {
327        if ( !writeDefaultKeyboardData( defaultKeyboardPath ) )
328            return Calamares::JobResult::error( tr( "Failed to write keyboard configuration to existing /etc/default directory." ),
329                                                tr( "Failed to write to %1" ).arg( keyboardConfPath ) );
330    }
331
332    return Calamares::JobResult::ok();
333}
Note: See TracBrowser for help on using the repository browser.