/* * Copyright (C) 2014 Hong Jen Yee (PCMan) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "sessionapplication.h" #include "sessiondbusadaptor.h" #include "lxqtmodman.h" #include "UdevNotifier.h" #include "numlock.h" #include "lockscreenmanager.h" #include #include #include #include #include "log.h" #include // XKB, this should be disabled in Wayland? #include SessionApplication::SessionApplication(int& argc, char** argv) : LXQt::Application(argc, argv), lockScreenManager(new LockScreenManager(this)) { listenToUnixSignals({SIGINT, SIGTERM, SIGQUIT, SIGHUP}); char* winmanager = NULL; int c; while ((c = getopt (argc, argv, "c:w:")) != -1) { if (c == 'c') { configName = optarg; break; } else if (c == 'w') { winmanager = optarg; break; } } if(configName.isEmpty()) configName = "session"; // tell the world which config file we're using. qputenv("LXQT_SESSION_CONFIG", configName.toUtf8()); modman = new LXQtModuleManager(winmanager); connect(this, &LXQt::Application::unixSignal, modman, &LXQtModuleManager::logout); new SessionDBusAdaptor(modman); // connect to D-Bus and register as an object: QDBusConnection::sessionBus().registerService("org.lxqt.session"); QDBusConnection::sessionBus().registerObject("/LXQtSession", modman); // Wait until the event loop starts QTimer::singleShot(0, this, SLOT(startup())); } SessionApplication::~SessionApplication() { delete modman; } bool SessionApplication::startup() { LXQt::Settings settings(configName); qCDebug(SESSION) << __FILE__ << ":" << __LINE__ << "Session" << configName << "about to launch (default 'session')"; loadEnvironmentSettings(settings); // loadFontSettings(settings); loadKeyboardSettings(settings); loadMouseSettings(settings); #if defined(WITH_LIBUDEV_MONITOR) UdevNotifier * dev_notifier = new UdevNotifier{QStringLiteral("input"), this}; //will be released upon our destruction QTimer * dev_timer = new QTimer{this}; //will be released upon our destruction dev_timer->setSingleShot(true); dev_timer->setInterval(500); //give some time to xorg... we need to reset keyboard afterwards connect(dev_timer, &QTimer::timeout, [this] { //XXX: is this a race? (because settings can be currently changed by lxqt-config-input) // but with such a little probablity we can live... LXQt::Settings settings(configName); loadKeyboardSettings(settings); }); connect(dev_notifier, &UdevNotifier::deviceAdded, [this, dev_timer] (QString device) { qCWarning(SESSION) << QStringLiteral("Session '%1', new input device '%2', keyboard setting will be (optionaly) reloaded...").arg(configName).arg(device); dev_timer->start(); }); #endif bool lockBeforeSleep = settings.value(QLatin1String("lock_screen_before_power_actions"), true).toBool(); if (lockScreenManager->startup(lockBeforeSleep)) qCDebug(SESSION) << "LockScreenManager started successfully"; else qCWarning(SESSION) << "LockScreenManager couldn't start"; // launch module manager and autostart apps modman->startup(settings); return true; } void SessionApplication::mergeXrdb(const char* content, int len) { qCDebug(SESSION) << "xrdb:" << content; QProcess xrdb; xrdb.start("xrdb -merge -"); xrdb.write(content, len); xrdb.closeWriteChannel(); xrdb.waitForFinished(); } void SessionApplication::loadEnvironmentSettings(LXQt::Settings& settings) { // first - set some user defined environment variables (like TERM...) settings.beginGroup("Environment"); QByteArray envVal; Q_FOREACH (QString i, settings.childKeys()) { envVal = settings.value(i).toByteArray(); lxqt_setenv(i.toUtf8().constData(), envVal); } settings.endGroup(); } // FIXME: how to set keyboard layout in Wayland? void SessionApplication::setxkbmap(QString layout, QString variant, QString model, QStringList options) { QStringList args; if(!model.isEmpty()) { args << QStringLiteral("-model"); args << model; } if(!layout.isEmpty()) { args << QStringLiteral("-layout"); args << layout; if(!variant.isEmpty()) { args << QStringLiteral("-variant"); args << variant; } } if(!options.isEmpty()) { Q_FOREACH(const QString& option, options) { args << QStringLiteral("-option"); args << option; } } // execute the command line if (!args.isEmpty()) QProcess::startDetached(QStringLiteral("setxkbmap"), args); } void SessionApplication::loadKeyboardSettings(LXQt::Settings& settings) { qCDebug(SESSION) << settings.fileName(); settings.beginGroup("Keyboard"); XKeyboardControl values; /* Keyboard settings */ unsigned int delay, interval; if(XkbGetAutoRepeatRate(QX11Info::display(), XkbUseCoreKbd, (unsigned int*) &delay, (unsigned int*) &interval)) { delay = settings.value("delay", delay).toUInt(); interval = settings.value("interval", interval).toUInt(); XkbSetAutoRepeatRate(QX11Info::display(), XkbUseCoreKbd, delay, interval); } // turn on/off keyboard beep bool beep = settings.value("beep").toBool(); values.bell_percent = beep ? -1 : 0; XChangeKeyboardControl(QX11Info::display(), KBBellPercent, &values); // turn on numlock as needed if(settings.value("numlock").toBool()) enableNumlock(); // keyboard layout support using setxkbmap QString layout = settings.value("layout").toString(); QString variant = settings.value("variant").toString(); QString model = settings.value("model").toString(); QStringList options = settings.value("options").toStringList(); setxkbmap(layout, variant, model, options); settings.endGroup(); } void SessionApplication::loadMouseSettings(LXQt::Settings& settings) { settings.beginGroup("Mouse"); // mouse cursor (does this work?) QString cursorTheme = settings.value("cursor_theme").toString(); int cursorSize = settings.value("cursor_size").toInt(); QByteArray buf; if(!cursorTheme.isEmpty()) { buf += "Xcursor.theme:"; buf += cursorTheme; buf += '\n'; } if(cursorSize > 0) { buf += "Xcursor.size:"; buf += QString("%1").arg(cursorSize); buf += '\n'; } if(!buf.isEmpty()) { buf += "Xcursor.theme_core:true\n"; mergeXrdb(buf.constData(), buf.length()); } // other mouse settings int accel_factor = settings.value("accel_factor").toInt(); int accel_threshold = settings.value("accel_threshold").toInt(); if(accel_factor || accel_threshold) XChangePointerControl(QX11Info::display(), accel_factor != 0, accel_threshold != 0, accel_factor, 10, accel_threshold); // left handed mouse? bool left_handed = settings.value("left_handed", false).toBool(); setLeftHandedMouse(left_handed); settings.endGroup(); } # if 0 // already deprecated by direct fontconfig support of lxqt-config void SessionApplication::loadFontSettings(LXQt::Settings& settings) { // set some Xft config values, such as antialiasing & subpixel // may call mergeXrdb() to do it. (will this work?) // font settings of gtk+ programs are controlled by gtkrc and settings.ini files. settings.beginGroup("Font"); QByteArray buf; bool antialias = settings.value("antialias", true).toBool(); buf += "Xft.antialias:"; buf += antialias ? "true" : "false"; buf += '\n'; buf += "Xft.rgba:"; buf += settings.value("subpixel", "none").toByteArray(); buf += '\n'; bool hinting = settings.value("hinting", true).toBool(); buf += "Xft.hinting:"; buf += hinting ? "true" : "false"; buf += '\n'; buf += "Xft.hintstyle:hint"; buf += settings.value("hint_style", "none").toByteArray(); buf += '\n'; int dpi = settings.value("dpi", 96).toInt(); buf += "Xft.dpi:"; buf += QString("%1").arg(dpi); buf += '\n'; mergeXrdb(buf.constData(), buf.length()); settings.endGroup(); } #endif /* This function is taken from Gnome's control-center 2.6.0.3 (gnome-settings-mouse.c) and was modified*/ #define DEFAULT_PTR_MAP_SIZE 128 void SessionApplication::setLeftHandedMouse(bool mouse_left_handed) { unsigned char *buttons, *more_buttons; int n_buttons, i; int idx_1 = 0, idx_3 = 1; buttons = (unsigned char*)malloc(DEFAULT_PTR_MAP_SIZE); if (!buttons) { return; } n_buttons = XGetPointerMapping(QX11Info::display(), buttons, DEFAULT_PTR_MAP_SIZE); if (n_buttons > DEFAULT_PTR_MAP_SIZE) { more_buttons = (unsigned char*)realloc(buttons, n_buttons); if (!more_buttons) { free(buttons); return; } buttons = more_buttons; n_buttons = XGetPointerMapping(QX11Info::display(), buttons, n_buttons); } for (i = 0; i < n_buttons; i++) { if (buttons[i] == 1) idx_1 = i; else if (buttons[i] == ((n_buttons < 3) ? 2 : 3)) idx_3 = i; } if ((mouse_left_handed && idx_1 < idx_3) || (!mouse_left_handed && idx_1 > idx_3)) { buttons[idx_1] = ((n_buttons < 3) ? 2 : 3); buttons[idx_3] = 1; XSetPointerMapping(QX11Info::display(), buttons, n_buttons); } free(buttons); }