1 //===========================================
2 //  Lumina-DE source code
3 //  Copyright (c) 2012-2018, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "LSession.h"
8 #include "global-objects.h"
9 
10 #include "BootSplash.h"
11 
12 #ifndef DEBUG
13 #define DEBUG 0
14 #endif
15 
16 //Initialize all the global objects to null pointers
17 NativeWindowSystem* Lumina::NWS = 0;
18 NativeEventFilter* Lumina::NEF = 0;
19 LScreenSaver* Lumina::SS = 0;
20 QThread* Lumina::EVThread = 0;
21 RootWindow* Lumina::ROOTWIN = 0;
22 XDGDesktopList* Lumina::APPLIST = 0;
23 LShortcutEvents* Lumina::SHORTCUTS = 0;
24 DesktopManager* Lumina::DESKMAN = 0;
25 
LSession(int & argc,char ** argv)26 LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lumina-desktop-unified"){
27   //Initialize the global objects to null pointers
28   qRegisterMetaType< Qt::Key >("Qt::Key");
29   qRegisterMetaType< NativeWindowObject::Property >("NativeWindowObject::Property");
30   qRegisterMetaType< QList< NativeWindowObject::Property > >("QList<NativeWindowObject::Property>");
31   qRegisterMetaType< NativeWindowSystem::MouseButton >("NativeWindowSystem::MouseButton");
32 
33   mediaObj = 0; //private object used for playing login/logout chimes
34  if(this->isPrimaryProcess()){
35   //Setup the global registrations
36   if(DEBUG){ qDebug() << "Starting session init..."; }
37   qsrand(QDateTime::currentMSecsSinceEpoch());
38   this->setApplicationName("Lumina Desktop Environment");
39   this->setApplicationVersion( LDesktopUtils::LuminaDesktopVersion() );
40   this->setOrganizationName("LuminaDesktopEnvironment");
41   this->setQuitOnLastWindowClosed(false); //since the LDesktop's are not necessarily "window"s
42   //Enable a few of the simple effects by default
43   if(DEBUG){ qDebug() << " - Setting attributes and effects"; }
44   this->setEffectEnabled( Qt::UI_AnimateMenu, true);
45   this->setEffectEnabled( Qt::UI_AnimateCombo, true);
46   this->setEffectEnabled( Qt::UI_AnimateTooltip, true);
47   this->setAttribute(Qt::AA_UseDesktopOpenGL);
48   this->setAttribute(Qt::AA_UseHighDpiPixmaps); //allow pixmaps to be scaled up as well as down
49 
50   //Now initialize the global objects (but do not start them yet)
51   if(DEBUG){ qDebug() << " - Create native event objects"; }
52   Lumina::NEF = new NativeEventFilter();
53   Lumina::NWS = new NativeWindowSystem();
54   Lumina::SS = new LScreenSaver();
55   Lumina::DESKMAN = new DesktopManager();
56   //Now put the Native Window System into it's own thread to keep things snappy
57   if(DEBUG){ qDebug() << " - Create extra threads"; }
58   Lumina::EVThread = new QThread();
59     Lumina::DESKMAN->moveToThread(Lumina::EVThread);
60   Lumina::EVThread->start();
61   Lumina::APPLIST = XDGDesktopList::instance();
62   if(DEBUG){ qDebug() << " - Create root window"; }
63   Lumina::ROOTWIN = new RootWindow();
64   Lumina::SHORTCUTS = new LShortcutEvents(); //this can be moved to it's own thread eventually as well
65   if(DEBUG){ qDebug() << " - Setup Connections"; }
66   setupGlobalConnections();
67  } //end check for primary process
68  if(DEBUG){ qDebug() << " [finished] Session init"; }
69 }
70 
~LSession()71 LSession::~LSession(){
72    //Clean up the global objects as needed
73   if(Lumina::NEF!=0){ Lumina::NEF->deleteLater(); }
74   if(Lumina::NWS!=0){ Lumina::NWS->deleteLater(); }
75   if(Lumina::SS!=0){ Lumina::SS->deleteLater(); }
76   if(Lumina::EVThread!=0){
77     if(Lumina::EVThread->isRunning()){ Lumina::EVThread->quit(); }
78     Lumina::EVThread->deleteLater();
79   }
80   if(DesktopSettings::instance()!=0){ DesktopSettings::instance()->deleteLater(); }
81   if(Lumina::ROOTWIN!=0){ Lumina::ROOTWIN->deleteLater(); }
82   if(Lumina::APPLIST!=0){ Lumina::APPLIST->deleteLater(); }
83   if(Lumina::DESKMAN!=0){ Lumina::DESKMAN->deleteLater(); }
84   if(OSInterface::instance()->isRunning()){ OSInterface::instance()->stop(); }
85   OSInterface::instance()->deleteLater();
86 }
87 
setupSession()88 void LSession::setupSession(){
89   BootSplash splash;
90     splash.showScreen("init");
91   qDebug() << "Setting up session:" << QDateTime::currentDateTime().toString( Qt::SystemLocaleShortDate);;
92   if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); }
93   QTime* timer = 0;
94   if(DEBUG){ timer = new QTime(); timer->start(); qDebug() << " - Init srand:" << timer->elapsed();}
95   //Seed random number generator
96   qsrand( QTime::currentTime().msec() );
97   //Connect internal signal/slots
98   connect(this, SIGNAL(InputsAvailable(QStringList)), this, SLOT(NewCommunication(QStringList)) );
99   //Setup the QSettings default paths
100     splash.showScreen("settings");
101   if(DEBUG){ qDebug() << " - Init QSettings:" << timer->elapsed();}
102   DesktopSettings::instance(); //don't do anything other than init/start the static object here
103   if(DEBUG){ qDebug() << " - Load Localization Files:" << timer->elapsed();}
104   currTranslator = LUtils::LoadTranslation(this, "lumina-desktop");
105   if(DEBUG){ qDebug() << " - Start Event Filter:" << timer->elapsed(); }
106   Lumina::NEF->start();
107   if( !Lumina::NWS->start() ){
108     qWarning() << "Could not start the Lumina desktop. Is another desktop or window manager running?";
109     this->exit(1);
110     return;
111   }
112 //use the system settings
113   //Setup the user's lumina settings directory as necessary
114     splash.showScreen("user");
115   if(DEBUG){ qDebug() << " - Init User Files:" << timer->elapsed();}
116   //checkUserFiles(); //adds these files to the watcher as well
117   Lumina::NWS->setRoot_numberOfWorkspaces(QStringList() << "one" << "two");
118   Lumina::NWS->setRoot_currentWorkspace(0);
119   OSInterface::instance()->start();
120 
121   Lumina::DESKMAN->start();
122   Lumina::ROOTWIN->start();
123   //Initialize the internal variables
124 
125 
126   //Start the background system tray
127     splash.showScreen("systray");
128   //Initialize the global menus
129   qDebug() << " - Initialize system menus";
130     splash.showScreen("apps");
131   if(DEBUG){ qDebug() << " - Populate App List:" << timer->elapsed();}
132   Lumina::APPLIST->updateList();
133 
134     splash.showScreen("menus");
135 
136 
137   //Initialize the desktops
138     splash.showScreen("desktop");
139 
140   //for(int i=0; i<6; i++){ LSession::processEvents(); } //Run through this a few times so the interface systems get up and running
141 
142   //Now setup the system watcher for changes
143     splash.showScreen("final");
144 
145   if(DEBUG){ qDebug() << " - Start Screen Saver:" << timer->elapsed();}
146   Lumina::SS->start();
147 
148   if(DEBUG){ qDebug() << " - Init Finished:" << timer->elapsed(); delete timer;}
149   Lumina::SHORTCUTS->start(); //Startup the shortcut handler now
150 
151   //for(int i=0; i<4; i++){ LSession::processEvents(); } //Again, just a few event loops here so thing can settle before we close the splash screen
152   //launchStartupApps();
153   QTimer::singleShot(500, this, SLOT(launchStartupApps()) );
154   splash.hide();
155   LSession::processEvents();
156   splash.close();
157   LSession::processEvents();
158   //DEBUG: Wait a bit then close down the session
159   //QTimer::singleShot(15000, this, SLOT(StartLogout()) );
160 }
161 
162 //================
163 //    PRIVATE
164 //================
CleanupSession()165 void LSession::CleanupSession(){
166   //Close any running applications and tray utilities (Make sure to keep the UI interactive)
167   LSession::processEvents();
168   QDateTime time = QDateTime::currentDateTime();
169   qDebug() << "Start closing down the session: " << time.toString( Qt::SystemLocaleShortDate);
170   //Create a temporary flag to prevent crash dialogs from opening during cleanup
171   LUtils::writeFile("/tmp/.luminastopping",QStringList() << "yes", true);
172   //Start the logout chimes (if necessary)
173   //LOS::setAudioVolume( LOS::audioVolume() ); //make sure the audio volume is saved in the backend for the next login
174   bool playaudio = DesktopSettings::instance()->value(DesktopSettings::Session,"PlayLogoutAudio",true).toBool();
175   if( playaudio ){ playAudioFile(LOS::LuminaShare()+"Logout.ogg"); }
176   //Now perform any other cleanup
177   //Lumina::NEF->stop();
178   //Now wait a moment for things to close down before quitting
179   if(playaudio && mediaObj!=0){
180     //wait a max of 5 seconds for audio to finish
181     bool waitmore = true;
182     for(int i=0; i<100 && waitmore; i++){
183       usleep(50000); //50ms = 50000 us
184       waitmore = (mediaObj->state()==QMediaPlayer::PlayingState);
185       LSession::processEvents();
186     }
187     if(waitmore){ mediaObj->stop(); } //timed out
188   }else{
189     for(int i=0; i<20; i++){ LSession::processEvents(); usleep(25000); } //1/2 second pause
190   }
191   //Clean up the temporary flag
192   if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); }
193 }
194 
195 //=================
196 
setupGlobalConnections()197 void LSession::setupGlobalConnections(){
198   //Setup the various connections between the global classes
199   // NOTE: Most of these connections will only become "active" as the global objects get started during the setupSession routine
200 
201   //Setup the basic connections between the shortcuts class and the session itself
202   connect(Lumina::SHORTCUTS, SIGNAL(StartLogout()), this, SLOT(StartLogout()) );
203   connect(Lumina::SHORTCUTS, SIGNAL(StartReboot()), this, SLOT(StartReboot()) );
204   connect(Lumina::SHORTCUTS, SIGNAL(StartShutdown()), this, SLOT(StartShutdown()) );
205   connect(Lumina::SHORTCUTS, SIGNAL(LaunchApplication(QString)), this, SLOT(LaunchApplication(QString)) );
206   connect(Lumina::SHORTCUTS, SIGNAL(LaunchStandardApplication(QString)), this, SLOT(LaunchStandardApplication(QString)) );
207   connect(Lumina::SHORTCUTS, SIGNAL(LockSession()), Lumina::SS, SLOT(LockScreenNow()) );
208 
209   //Root window connections
210   connect(Lumina::ROOTWIN, SIGNAL(RegisterVirtualRoot(WId)), Lumina::NWS, SLOT(RegisterVirtualRoot(WId)) );
211   connect(Lumina::ROOTWIN, SIGNAL(RootResized(QRect)), Lumina::NWS, SLOT(setRoot_desktopGeometry(QRect)) );
212   connect(RootDesktopObject::instance(), SIGNAL(mouseMoved()), Lumina::SS, SLOT(newInputEvent()) );
213   connect(RootDesktopObject::instance(), SIGNAL(startLogout()), this, SLOT(StartLogout()) );
214   connect(RootDesktopObject::instance(), SIGNAL(lockScreen()), Lumina::SS, SLOT(LockScreenNow()) );
215   connect(RootDesktopObject::instance(), SIGNAL(launchApplication(QString)), this, SLOT(LaunchStandardApplication(QString)) );
216 
217   //Native Window Class connections
218   connect(Lumina::NEF, SIGNAL(WindowCreated(WId)), Lumina::NWS, SLOT(NewWindowDetected(WId)));
219   connect(Lumina::NEF, SIGNAL(WindowDestroyed(WId)), Lumina::NWS, SLOT(WindowCloseDetected(WId)));
220   connect(Lumina::NEF, SIGNAL(WindowPropertyChanged(WId, NativeWindowObject::Property)), Lumina::NWS, SLOT(WindowPropertyChanged(WId, NativeWindowObject::Property)));
221   connect(Lumina::NEF, SIGNAL(WindowPropertiesChanged(WId, QList<NativeWindowObject::Property>)), Lumina::NWS, SLOT(WindowPropertiesChanged(WId, QList<NativeWindowObject::Property>)) );
222   connect(Lumina::NEF, SIGNAL(WindowPropertyChanged(WId, NativeWindowObject::Property, QVariant)), Lumina::NWS, SLOT(WindowPropertyChanged(WId, NativeWindowObject::Property, QVariant)));
223   connect(Lumina::NEF, SIGNAL(WindowPropertiesChanged(WId, QList<NativeWindowObject::Property>, QList<QVariant>)), Lumina::NWS, SLOT(WindowPropertiesChanged(WId, QList<NativeWindowObject::Property>, QList<QVariant>)) );
224   connect(Lumina::NEF, SIGNAL(RequestWindowPropertyChange(WId, NativeWindowObject::Property, QVariant)), Lumina::NWS, SLOT(RequestPropertyChange(WId, NativeWindowObject::Property, QVariant)));
225   connect(Lumina::NEF, SIGNAL(RequestWindowPropertiesChange(WId, QList<NativeWindowObject::Property>, QList<QVariant>)), Lumina::NWS, SLOT(RequestPropertiesChange(WId, QList<NativeWindowObject::Property>, QList<QVariant>)));
226   connect(Lumina::NEF, SIGNAL(TrayWindowCreated(WId)), Lumina::NWS, SLOT(NewTrayWindowDetected(WId)));
227   connect(Lumina::NEF, SIGNAL(TrayWindowDestroyed(WId)), Lumina::NWS, SLOT(WindowCloseDetected(WId)));
228   connect(Lumina::NEF, SIGNAL(PossibleDamageEvent(WId)), Lumina::NWS, SLOT(CheckDamageID(WId)));
229   connect(Lumina::NEF, SIGNAL(KeyPressed(int, WId)), Lumina::NWS, SLOT(NewKeyPress(int, WId)));
230   connect(Lumina::NEF, SIGNAL(KeyReleased(int, WId)), Lumina::NWS, SLOT(NewKeyRelease(int, WId)));
231   connect(Lumina::NEF, SIGNAL(MousePressed(int, WId)), Lumina::NWS, SLOT(NewMousePress(int, WId)));
232   connect(Lumina::NEF, SIGNAL(MouseReleased(int, WId)), Lumina::NWS, SLOT(NewMouseRelease(int, WId)));
233   //connect(Lumina::NEF, SIGNAL(MouseMovement(WId)), Lumina::NWS, SLOT());
234   //connect(Lumina::NEF, SIGNAL(MouseEnterWindow(WId)), Lumina::NWS, SLOT());
235   //connect(Lumina::NEF, SIGNAL(MouseLeaveWindow(WId)), Lumina::NWS, SLOT());
236 
237   //Input Events for ScreenSaver
238   connect(Lumina::NEF, SIGNAL(KeyPressed(int, WId)), Lumina::SS, SLOT(newInputEvent()));
239   connect(Lumina::NEF, SIGNAL(KeyReleased(int, WId)), Lumina::SS, SLOT(newInputEvent()));
240   connect(Lumina::NEF, SIGNAL(MousePressed(int, WId)), Lumina::SS, SLOT(newInputEvent()));
241   connect(Lumina::NEF, SIGNAL(MouseReleased(int, WId)), Lumina::SS, SLOT(newInputEvent()));
242   connect(Lumina::NEF, SIGNAL(MouseMovement()), Lumina::SS, SLOT(newInputEvent()));
243 
244   connect(Lumina::SS, SIGNAL(LockStatusChanged(bool)), Lumina::NWS, SLOT(ScreenLockChanged(bool)) );
245 
246   //Mouse/Keyboard Shortcut Events (Make sure to connect to the NWS - the raw events need to be ignored sometimes)
247   connect(Lumina::NWS, SIGNAL(KeyPressDetected(WId, Qt::Key)), Lumina::SHORTCUTS, SLOT(KeyPress(WId, Qt::Key)) );
248   connect(Lumina::NWS, SIGNAL(KeyReleaseDetected(WId, Qt::Key)), Lumina::SHORTCUTS, SLOT(KeyRelease(WId, Qt::Key)) );
249   connect(Lumina::NWS, SIGNAL(MousePressDetected(WId, NativeWindowSystem::MouseButton)), Lumina::SHORTCUTS, SLOT(MousePress(WId, NativeWindowSystem::MouseButton)) );
250   connect(Lumina::NWS, SIGNAL(MouseReleaseDetected(WId, NativeWindowSystem::MouseButton)), Lumina::SHORTCUTS, SLOT(MouseRelease(WId, NativeWindowSystem::MouseButton)) );
251 
252   //NWS Events to the window system
253   connect(Lumina::NWS, SIGNAL(NewWindowAvailable(NativeWindowObject*)), Lumina::DESKMAN, SLOT(NewWindowAvailable(NativeWindowObject*)) );
254   connect(Lumina::NWS, SIGNAL(WindowClosed()), Lumina::DESKMAN, SLOT(syncWindowList()) );
255   connect(Lumina::NWS, SIGNAL(NewTrayWindowAvailable(NativeWindowObject*)), Lumina::DESKMAN, SLOT(NewTrayWindowAvailable(NativeWindowObject*)) );
256   connect(Lumina::NWS, SIGNAL(TrayWindowClosed()), Lumina::DESKMAN, SLOT(syncTrayWindowList()) );
257 
258 }
259 
260 //=================
261 
VersionStringToNumber(QString version)262 int LSession::VersionStringToNumber(QString version){
263   version = version.section("-",0,0); //trim any extra labels off the end
264   int maj, mid, min; //major/middle/minor version numbers (<Major>.<Middle>.<Minor>)
265   maj = mid = min = 0;
266   bool ok = true;
267   maj = version.section(".",0,0).toInt(&ok);
268   if(ok){ mid = version.section(".",1,1).toInt(&ok); }else{ maj = 0; }
269   if(ok){ min = version.section(".",2,2).toInt(&ok); }else{ mid = 0; }
270   if(!ok){ min = 0; }
271   //Now assemble the number
272   //NOTE: This format allows numbers to be anywhere from 0->999 without conflict
273   return (maj*1000000 + mid*1000 + min);
274 }
275 
276 //=================
277 
278 //Play System Audio
playAudioFile(QString filepath)279 void LSession::playAudioFile(QString filepath){
280   if( !QFile::exists(filepath) ){ return; }
281   //Setup the audio output systems for the desktop
282   if(DEBUG){ qDebug() << "Play Audio File"; }
283   if(mediaObj==0){   qDebug() << " - Initialize media player"; mediaObj = new QMediaPlayer(0,QMediaPlayer::LowLatency); }
284   if(mediaObj !=0 ){
285     if(DEBUG){ qDebug() << " - starting playback:" << filepath; }
286     mediaObj->setVolume(100);
287     mediaObj->setMedia(QUrl::fromLocalFile(filepath));
288     mediaObj->play();
289     LSession::processEvents();
290   }
291   if(DEBUG){ qDebug() << " - Done with Audio File"; }
292 }
293 
294 //================
295 //  PRIVATE SLOTS
296 //================
NewCommunication(QStringList list)297 void LSession::NewCommunication(QStringList list){
298   if(DEBUG){ qDebug() << "New Communications:" << list; }
299   for(int i=0; i<list.length(); i++){
300     if(list[i]=="--logout"){
301       QTimer::singleShot(0, this, SLOT(StartLogout()) );
302     }else if(list[i]=="--lock-session"){
303       Lumina::SS->LockScreenNow();
304     }
305   }
306 }
307 
launchStartupApps()308 void LSession::launchStartupApps(){
309   //First start any system-defined startups, then do user defined
310   qDebug() << "Launching startup applications";
311 
312   //Enable Numlock
313   if(LUtils::isValidBinary("numlockx")){ //make sure numlockx is installed
314     if(DesktopSettings::instance()->value(DesktopSettings::System,"EnableNumlock",false).toBool()){
315       QProcess::startDetached("numlockx on");
316     }else{
317       QProcess::startDetached("numlockx off");
318     }
319   }
320   /*int tmp = LOS::ScreenBrightness();
321   if(tmp>0){
322     LOS::setScreenBrightness( tmp );
323     qDebug() << " - - Screen Brightness:" << QString::number(tmp)+"%";
324   }
325   //ExternalProcess::launch("nice lumina-open -autostart-apps");
326 
327   //Re-load the screen brightness and volume settings from the previous session
328   // Wait until after the XDG-autostart functions, since the audio system might be started that way
329   qDebug() << " - Loading previous settings";
330   tmp = LOS::audioVolume();
331   LOS::setAudioVolume(tmp);
332   qDebug() << " - - Audio Volume:" << QString::number(tmp)+"%";
333   */
334   //Now play the login music since we are finished
335   if(DesktopSettings::instance()->value(DesktopSettings::System,"PlayStartupAudio",true).toBool()){
336     //Make sure to re-set the system volume to the last-used value at outset
337     /*int vol = LOS::audioVolume();
338     if(vol>=0){ LOS::setAudioVolume(vol); }*/
339     LSession::playAudioFile(LOS::LuminaShare()+"Login.ogg");
340   }
341   qDebug() << " - Finished with startup routines";
342 }
343 
checkUserFiles()344 void LSession::checkUserFiles(){
345   //internal version conversion examples:
346   //  [1.0.0 -> 1000000], [1.2.3 -> 1002003], [0.6.1 -> 6001]
347   /*QString OVS = DesktopSettings::instance()->value(DesktopSettings::System,"DesktopVersion","0").toString(); //Old Version String
348   bool changed = LDesktopUtils::checkUserFiles(OVS);
349   if(changed){
350     //Save the current version of the session to the settings file (for next time)
351     DesktopSettings::instance()->setValue(DesktopSettings::System,"DesktopVersion", this->applicationVersion());
352   }*/
353 }
354 
355 
356 //==================
357 //  PUBLIC SLOTS
358 //==================
StartLogout()359 void LSession::StartLogout(){
360   CleanupSession();
361   QCoreApplication::exit(0);
362 }
363 
StartShutdown(bool skipupdates)364 void LSession::StartShutdown(bool skipupdates){
365   CleanupSession();
366   LOS::systemShutdown(skipupdates);
367   QCoreApplication::exit(0);
368 }
369 
StartReboot(bool skipupdates)370 void LSession::StartReboot(bool skipupdates){
371   CleanupSession();
372   LOS::systemRestart(skipupdates);
373   QCoreApplication::exit(0);
374 }
375 
LaunchApplication(QString exec)376 void LSession::LaunchApplication(QString exec){
377   qDebug() << "Launch Application:" << exec;
378   ExternalProcess::launch(exec);
379 }
380 
LaunchDesktopApplication(QString app,QString action)381 void LSession::LaunchDesktopApplication(QString app, QString action){
382   qDebug() << "Launch Desktop Application:" << app << action;
383   XDGDesktop *xdg = Lumina::APPLIST->findAppFile(app);
384   bool cleanup = false;
385   if(xdg==0){
386     xdg = new XDGDesktop(app);
387     cleanup = true;
388   }
389   if(xdg->isValid()){
390     QString exec = xdg->generateExec(QStringList(), action);
391     ExternalProcess::launch(exec, QStringList(), xdg->startupNotify);
392   }
393 
394   if(cleanup && xdg!=0){ xdg->deleteLater(); }
395 }
396 
LaunchStandardApplication(QString app,QStringList args)397 void LSession::LaunchStandardApplication(QString app, QStringList args){
398   qDebug() << "Launch Standard Application:" << app << args;
399   //Find/replace standardized apps with thier mimetypes
400   if(app.startsWith("--")){ app = "application/"+app.section("--",-1).simplified(); }
401   //First see if this is a mimetype with a default application
402   if(app.count("/")==1 && !app.startsWith("/")){
403     QString mimeapp = XDGMime::findDefaultAppForMime(app);
404     if(!mimeapp.isEmpty()){ app = mimeapp; }
405   }
406   if(!app.endsWith(".desktop")){
407     //actual command/binary - just launch it
408     ExternalProcess::launch(app, args, false); // do not use startup notify cursor
409   }else{
410     //Get the XDGDesktop structure
411     XDGDesktop *desk = 0; bool cleanup = false;
412     if(app.startsWith("/") && QFile::exists(app)){ desk = new XDGDesktop(app); cleanup = true; }
413     if(desk==0 || !desk->isValid()){
414       //Need to find the app within the current list
415       if(cleanup){ desk->deleteLater(); desk = 0; cleanup = false; }
416       app = app.section("/",-1); //make sure this is a relative path
417       desk = Lumina::APPLIST->findAppFile(app);
418     }
419     if(desk!=0 && desk->isValid()){
420       //Got the application - go ahead and assemble the startup command
421       QString exec = desk->generateExec(args); //args are embedded into the exec command as needed
422       ExternalProcess::launch(exec, QStringList(), desk->startupNotify);
423     }
424     if(cleanup){ desk->deleteLater(); }
425   }
426 
427 }
428 
reloadIconTheme()429 void LSession::reloadIconTheme(){
430   //Wait a moment for things to settle before sending out the signal to the interfaces
431   QApplication::processEvents();
432   QApplication::processEvents();
433   emit IconThemeChanged();
434 }
435 
436 //Temporarily change the session locale (nothing saved between sessions)
switchLocale(QString localeCode)437 void LSession::switchLocale(QString localeCode){
438   currTranslator = LUtils::LoadTranslation(this, "lumina-desktop", localeCode, currTranslator);
439   if(currTranslator!=0 || localeCode=="en_US"){
440     LUtils::setLocaleEnv(localeCode); //will set everything to this locale (no custom settings)
441   }
442   emit LocaleChanged();
443 }
444