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