/*
* Copyright ( C ) 2014 Hong Jen Yee ( PCMan ) < pcman . tw @ gmail . com >
*
* 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 <unistd.h>
# include <csignal>
# include <LXQt/Settings>
# include <QProcess>
# include "log.h"
# include <QX11Info>
// XKB, this should be disabled in Wayland?
# include <X11/XKBlib.h>
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 ) ;
}