aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/lumina-desktop-unified/LSession.cpp
blob: 3226fa82f8b06b15ad33c6c912bf260799ef9575 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
//===========================================
//  Lumina-DE source code
//  Copyright (c) 2012-2017, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "LSession.h"
#include "global-objects.h"

#include "src-desktop/ContextMenu.h"

#include "BootSplash.h"
#ifndef DEBUG
#define DEBUG 1
#endif

//Initialize all the global objects to null pointers
NativeWindowSystem* Lumina::NWS = 0;
NativeEventFilter* Lumina::NEF = 0;
//EventFilter* Lumina::EFILTER = 0;
LScreenSaver* Lumina::SS = 0;
DesktopSettings* Lumina::SETTINGS = 0;
//Lumina::WM = 0;
QThread* Lumina::EVThread = 0;
RootWindow* Lumina::ROOTWIN = 0;
XDGDesktopList* Lumina::APPLIST = 0;
LShortcutEvents* Lumina::SHORTCUTS = 0;

LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lumina-desktop-unified"){
  //Initialize the global objects to null pointers
  mediaObj = 0; //private object used for playing login/logout chimes
 if(this->isPrimaryProcess()){
  //Setup the global registrations
  qsrand(QDateTime::currentMSecsSinceEpoch());
  this->setApplicationName("Lumina Desktop Environment");
  this->setApplicationVersion( LDesktopUtils::LuminaDesktopVersion() );
  this->setOrganizationName("LuminaDesktopEnvironment");
  this->setQuitOnLastWindowClosed(false); //since the LDesktop's are not necessarily "window"s
  //Enable a few of the simple effects by default
  this->setEffectEnabled( Qt::UI_AnimateMenu, true);
  this->setEffectEnabled( Qt::UI_AnimateCombo, true);
  this->setEffectEnabled( Qt::UI_AnimateTooltip, true);
  //this->setAttribute(Qt::AA_UseDesktopOpenGL);
  this->setAttribute(Qt::AA_UseHighDpiPixmaps); //allow pixmaps to be scaled up as well as down

  //Now initialize the global objects (but do not start them yet)
  Lumina::NEF = new NativeEventFilter();
  Lumina::NWS = new NativeWindowSystem();
  //Lumina::EFILTER = new EventFilter(); //Need the XCB Event filter first
  Lumina::SETTINGS = new DesktopSettings();
  Lumina::SS = new LScreenSaver();
  //Lumina::WM = new LWindowManager();
  //Now put the Event Filter into it's own thread to keep things snappy
  Lumina::EVThread = new QThread();
    Lumina::NEF->moveToThread(Lumina::EVThread);
  Lumina::EVThread->start();
  Lumina::ROOTWIN = new RootWindow();
  Lumina::APPLIST = new XDGDesktopList(0, true); //keep this list up to date
  Lumina::SHORTCUTS = new LShortcutEvents(); //this can be moved to it's own thread eventually as well

  setupGlobalConnections();
 } //end check for primary process
}

LSession::~LSession(){
   //Clean up the global objects as needed
  if(Lumina::NEF!=0){ Lumina::NEF->deleteLater(); }
  if(Lumina::NWS!=0){ Lumina::NWS->deleteLater(); }
  //if(Lumina::EFILTER!=0){ Lumina::EFILTER->deleteLater(); }
  if(Lumina::SS!=0){ Lumina::SS->deleteLater(); }
  if(Lumina::EVThread!=0){
    if(Lumina::EVThread->isRunning()){ Lumina::EVThread->quit(); }
    Lumina::EVThread->deleteLater();
  }
  if(Lumina::SETTINGS!=0){ Lumina::SETTINGS->deleteLater(); }
  if(Lumina::ROOTWIN!=0){ Lumina::ROOTWIN->deleteLater(); }
  if(Lumina::APPLIST!=0){ Lumina::APPLIST->deleteLater(); }
}

void LSession::setupSession(){
  BootSplash splash;
    splash.showScreen("init");
  qDebug() << "Initializing Session:" << QDateTime::currentDateTime().toString( Qt::SystemLocaleShortDate);;
  if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); }
  QTime* timer = 0;
  if(DEBUG){ timer = new QTime(); timer->start(); qDebug() << " - Init srand:" << timer->elapsed();}
  //Seed random number generator
  qsrand( QTime::currentTime().msec() );
  //Connect internal signal/slots
  connect(this, SIGNAL(InputsAvailable(QStringList)), this, SLOT(NewCommunication(QStringList)) );
  //Setup the QSettings default paths
    splash.showScreen("settings");
  if(DEBUG){ qDebug() << " - Init QSettings:" << timer->elapsed();}
  Lumina::SETTINGS->start();
  /*sessionsettings = new QSettings("lumina-desktop", "sessionsettings");
  DPlugSettings = new QSettings("lumina-desktop","pluginsettings/desktopsettings");
  //Load the proper translation files
  if(sessionsettings->value("ForceInitialLocale",false).toBool()){
    //Some system locale override it in place - change the env first
    LUtils::setLocaleEnv( sessionsettings->value("InitLocale/LANG","").toString(), \
				sessionsettings->value("InitLocale/LC_MESSAGES","").toString(), \
				sessionsettings->value("InitLocale/LC_TIME","").toString(), \
				sessionsettings->value("InitLocale/LC_NUMERIC","").toString(), \
				sessionsettings->value("InitLocale/LC_MONETARY","").toString(), \
				sessionsettings->value("InitLocale/LC_COLLATE","").toString(), \
				sessionsettings->value("InitLocale/LC_CTYPE","").toString() );
  }*/
  if(DEBUG){ qDebug() << " - Load Localization Files:" << timer->elapsed();}
  currTranslator = LUtils::LoadTranslation(this, "lumina-desktop");
  if(DEBUG){ qDebug() << " - Start Event Filter:" << timer->elapsed(); }
  Lumina::NEF->start();
  if( !Lumina::NWS->start() ){
    qWarning() << "Could not start the Lumina desktop. Is another desktop or window manager running?";
    this->exit(1);
    return;
  }
//use the system settings
  //Setup the user's lumina settings directory as necessary
    splash.showScreen("user");
  if(DEBUG){ qDebug() << " - Init User Files:" << timer->elapsed();}
  //checkUserFiles(); //adds these files to the watcher as well

  //Initialize the internal variables
  //DESKTOPS.clear();

  //Start the background system tray
    splash.showScreen("systray");
  //Initialize the global menus
  qDebug() << " - Initialize system menus";
    splash.showScreen("apps");
  if(DEBUG){ qDebug() << " - Populate App List:" << timer->elapsed();}
  Lumina::APPLIST->updateList();
  //appmenu = new AppMenu();

    splash.showScreen("menus");
  //if(DEBUG){ qDebug() << " - Init SettingsMenu:" << timer->elapsed();}
  //settingsmenu = new SettingsMenu();
  //if(DEBUG){ qDebug() << " - Init SystemWindow:" << timer->elapsed();}
  //sysWindow = new SystemWindow();

  //Initialize the desktops
    splash.showScreen("desktop");
  if(DEBUG){ qDebug() << " - Init Desktops:" << timer->elapsed(); }
  QList<QScreen*> scrns= QApplication::screens();
  for(int i=0; i<scrns.length(); i++){
    qDebug() << "   --- Load Wallpaper for Screen:" << scrns[i]->name();
    Lumina::ROOTWIN->ChangeWallpaper(scrns[i]->name(), RootWindow::Stretch, LOS::LuminaShare()+"desktop-background.jpg");
  }
  Lumina::ROOTWIN->start();
  DesktopContextMenu *cmenu = new DesktopContextMenu(Lumina::ROOTWIN);
  connect(cmenu, SIGNAL(showLeaveDialog()), this, SLOT(StartLogout()) );
  cmenu->start();

  //desktopFiles = QDir(QDir::homePath()+"/Desktop").entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs, QDir::Name | QDir::IgnoreCase | QDir::DirsFirst);
  //updateDesktops();
  //for(int i=0; i<6; i++){ LSession::processEvents(); } //Run through this a few times so the interface systems get up and running

  //Now setup the system watcher for changes
    splash.showScreen("final");
  //if(DEBUG){ qDebug() << " - Init QFileSystemWatcher:" << timer->elapsed();}
  /*watcher = new QFileSystemWatcher(this);
    QString confdir = sessionsettings->fileName().section("/",0,-2);
    watcherChange(sessionsettings->fileName() );
    watcherChange( confdir+"/desktopsettings.conf" );
    watcherChange( confdir+"/fluxbox-init" );
    watcherChange( confdir+"/fluxbox-keys" );
    watcherChange( confdir+"/favorites.list" );
    //Try to watch the localized desktop folder too
    if(QFile::exists(QDir::homePath()+"/"+tr("Desktop"))){ watcherChange( QDir::homePath()+"/"+tr("Desktop") ); }
    watcherChange( QDir::homePath()+"/Desktop" );*/

  //connect internal signals/slots
  //connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherChange(QString)) );
  //connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange(QString)) );
  //connect(this, SIGNAL(aboutToQuit()), this, SLOT(SessionEnding()) );
  if(DEBUG){ qDebug() << " - Start Screen Saver:" << timer->elapsed();}
  Lumina::SS->start();

  if(DEBUG){ qDebug() << " - Init Finished:" << timer->elapsed(); delete timer;}
  Lumina::SHORTCUTS->start(); //Startup the shortcut handler now
  //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
  //launchStartupApps();
  QTimer::singleShot(500, this, SLOT(launchStartupApps()) );
  splash.hide();
  LSession::processEvents();
  splash.close();
  LSession::processEvents();
  //DEBUG: Wait a bit then close down the session
  //QTimer::singleShot(15000, this, SLOT(StartLogout()) );
}

//================
//    PRIVATE
//================
void LSession::CleanupSession(){
  //Close any running applications and tray utilities (Make sure to keep the UI interactive)
  LSession::processEvents();
  QDateTime time = QDateTime::currentDateTime();
  qDebug() << "Start closing down the session: " << time.toString( Qt::SystemLocaleShortDate);
  //Create a temporary flag to prevent crash dialogs from opening during cleanup
  LUtils::writeFile("/tmp/.luminastopping",QStringList() << "yes", true);
  //Start the logout chimes (if necessary)
  LOS::setAudioVolume( LOS::audioVolume() ); //make sure the audio volume is saved in the backend for the next login
  bool playaudio = Lumina::SETTINGS->value(DesktopSettings::Session,"PlayLogoutAudio",true).toBool();
  if( playaudio ){ playAudioFile(LOS::LuminaShare()+"Logout.ogg"); }
  //Now perform any other cleanup
  //Lumina::NEF->stop();
  //Now wait a moment for things to close down before quitting
  if(playaudio){
    //wait a max of 5 seconds for audio to finish
    bool waitmore = true;
    for(int i=0; i<100 && waitmore; i++){
      usleep(50000); //50ms = 50000 us
      waitmore = (mediaObj->state()==QMediaPlayer::PlayingState);
      LSession::processEvents();
    }
    if(waitmore){ mediaObj->stop(); } //timed out
  }else{
    for(int i=0; i<20; i++){ LSession::processEvents(); usleep(25000); } //1/2 second pause
  }
  //Clean up the temporary flag
  if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); }
}

//=================

void LSession::setupGlobalConnections(){
  //Setup the various connections between the global classes
  // NOTE: Most of these connections will only become "active" as the global objects get started during the setupSession routine

  //Setup the basic connections between the shortcuts class and the session itself
  connect(Lumina::SHORTCUTS, SIGNAL(StartLogout()), this, SLOT(StartLogout()) );
  connect(Lumina::SHORTCUTS, SIGNAL(StartReboot()), this, SLOT(StartReboot()) );
  connect(Lumina::SHORTCUTS, SIGNAL(StartShutdown()), this, SLOT(StartShutdown()) );

  //Root window connections
  connect(Lumina::ROOTWIN, SIGNAL(RegisterVirtualRoot(WId)), Lumina::NWS, SLOT(RegisterVirtualRoot(WId)) );

  //Native Window Class connections
  connect(Lumina::NEF, SIGNAL(WindowCreated(WId)), Lumina::NWS, SLOT(NewWindowDetected(WId)));
  connect(Lumina::NEF, SIGNAL(WindowDestroyed(WId)), Lumina::NWS, SLOT(WindowCloseDetected(WId)));
  connect(Lumina::NEF, SIGNAL(WindowPropertyChanged(WId, NativeWindow::Property)), Lumina::NWS, SLOT(WindowPropertyChanged(WId, NativeWindow::Property)));
  connect(Lumina::NEF, SIGNAL(TrayWindowCreated(WId)), Lumina::NWS, SLOT(NewTrayWindowDetected(WId)));
  connect(Lumina::NEF, SIGNAL(TrayWindowDestroyed(WId)), Lumina::NWS, SLOT(WindowCloseDetected(WId)));
  connect(Lumina::NEF, SIGNAL(PossibleDamageEvent(WId)), Lumina::NWS, SLOT(CheckDamageID(WId)));
  connect(Lumina::NEF, SIGNAL(KeyPressed(int, WId)), Lumina::NWS, SLOT(NewKeyPress(int, WId)));
  connect(Lumina::NEF, SIGNAL(KeyReleased(int, WId)), Lumina::NWS, SLOT(NewKeyRelease(int, WId)));
  connect(Lumina::NEF, SIGNAL(MousePressed(int, WId)), Lumina::NWS, SLOT(NewMousePress(int, WId)));
  connect(Lumina::NEF, SIGNAL(MouseReleased(int, WId)), Lumina::NWS, SLOT(NewMouseRelease(int, WId)));
  //connect(Lumina::NEF, SIGNAL(MouseMovement(WId)), Lumina::NWS, SLOT());
  //connect(Lumina::NEF, SIGNAL(MouseEnterWindow(WId)), Lumina::NWS, SLOT());
  //connect(Lumina::NEF, SIGNAL(MouseLeaveWindow(WId)), Lumina::NWS, SLOT());

  //Input Events for ScreenSaver
  connect(Lumina::NEF, SIGNAL(KeyPressed(int, WId)), Lumina::SS, SLOT(newInputEvent()));
  connect(Lumina::NEF, SIGNAL(KeyReleased(int, WId)), Lumina::SS, SLOT(newInputEvent()));
  connect(Lumina::NEF, SIGNAL(MousePressed(int, WId)), Lumina::SS, SLOT(newInputEvent()));
  connect(Lumina::NEF, SIGNAL(MouseReleased(int, WId)), Lumina::SS, SLOT(newInputEvent()));
  connect(Lumina::NEF, SIGNAL(MouseMovement()), Lumina::SS, SLOT(newInputEvent()));

  connect(Lumina::SS, SIGNAL(LockStatusChanged(bool)), Lumina::NWS, SLOT(ScreenLockChanged(bool)) );

  //Mouse/Keyboard Shortcut Events (Make sure to connect to the NWS - the raw events need to be ignored sometimes)
  connect(Lumina::NWS, SIGNAL(KeyPressDetected(WId, int)), Lumina::SHORTCUTS, SLOT(KeyPress(WId, int)) );
  connect(Lumina::NWS, SIGNAL(KeyReleaseDetected(WId, int)), Lumina::SHORTCUTS, SLOT(KeyRelease(WId, int)) );
  connect(Lumina::NWS, SIGNAL(MousePressDetected(WId, NativeWindowSystem::MouseButton)), Lumina::SHORTCUTS, SLOT(MousePress(WId, NativeWindowSystem::MouseButton)) );
  connect(Lumina::NWS, SIGNAL(MouseReleaseDetected(WId, NativeWindowSystem::MouseButton)), Lumina::SHORTCUTS, SLOT(MouseRelease(WId, NativeWindowSystem::MouseButton)) );

  //NWS Events to the window system
  connect(Lumina::NWS, SIGNAL(NewWindowAvailable(NativeWindow*)), Lumina::ROOTWIN, SLOT(NewWindow(NativeWindow*)) );
}

//=================

int LSession::VersionStringToNumber(QString version){
  version = version.section("-",0,0); //trim any extra labels off the end
  int maj, mid, min; //major/middle/minor version numbers (<Major>.<Middle>.<Minor>)
  maj = mid = min = 0;
  bool ok = true;
  maj = version.section(".",0,0).toInt(&ok);
  if(ok){ mid = version.section(".",1,1).toInt(&ok); }else{ maj = 0; }
  if(ok){ min = version.section(".",2,2).toInt(&ok); }else{ mid = 0; }
  if(!ok){ min = 0; }
  //Now assemble the number
  //NOTE: This format allows numbers to be anywhere from 0->999 without conflict
  return (maj*1000000 + mid*1000 + min);
}

//=================

//Play System Audio
void LSession::playAudioFile(QString filepath){
  if( !QFile::exists(filepath) ){ return; }
  //Setup the audio output systems for the desktop
  if(DEBUG){ qDebug() << "Play Audio File"; }
  if(mediaObj==0){   qDebug() << " - Initialize media player"; mediaObj = new QMediaPlayer(); }
  if(mediaObj !=0 ){
    if(DEBUG){ qDebug() << " - starting playback:" << filepath; }
    mediaObj->setVolume(100);
    mediaObj->setMedia(QUrl::fromLocalFile(filepath));
    mediaObj->play();
    LSession::processEvents();
  }
  if(DEBUG){ qDebug() << " - Done with Audio File"; }
}

//================
//  PRIVATE SLOTS
//================
void LSession::NewCommunication(QStringList list){
  if(DEBUG){ qDebug() << "New Communications:" << list; }
  for(int i=0; i<list.length(); i++){
    if(list[i]=="--logout"){
      QTimer::singleShot(0, this, SLOT(StartLogout()) );
    }else if(list[i]=="--lock-session"){
      Lumina::SS->LockScreenNow();
    }
  }
}

void LSession::launchStartupApps(){
  //First start any system-defined startups, then do user defined
  qDebug() << "Launching startup applications";

  //Enable Numlock
  if(LUtils::isValidBinary("numlockx")){ //make sure numlockx is installed
    if(Lumina::SETTINGS->value(DesktopSettings::System,"EnableNumlock",false).toBool()){
      QProcess::startDetached("numlockx on");
    }else{
      QProcess::startDetached("numlockx off");
    }
  }
  /*int tmp = LOS::ScreenBrightness();
  if(tmp>0){
    LOS::setScreenBrightness( tmp );
    qDebug() << " - - Screen Brightness:" << QString::number(tmp)+"%";
  }
  //ExternalProcess::launch("nice lumina-open -autostart-apps");

  //Re-load the screen brightness and volume settings from the previous session
  // Wait until after the XDG-autostart functions, since the audio system might be started that way
  qDebug() << " - Loading previous settings";
  tmp = LOS::audioVolume();
  LOS::setAudioVolume(tmp);
  qDebug() << " - - Audio Volume:" << QString::number(tmp)+"%";
  */
  //Now play the login music since we are finished
  if(Lumina::SETTINGS->value(DesktopSettings::System,"PlayStartupAudio",true).toBool()){
    //Make sure to re-set the system volume to the last-used value at outset
    /*int vol = LOS::audioVolume();
    if(vol>=0){ LOS::setAudioVolume(vol); }*/
    LSession::playAudioFile(LOS::LuminaShare()+"Login.ogg");
  }
  qDebug() << " - Finished with startup routines";
}

void LSession::checkUserFiles(){
  //internal version conversion examples:
  //  [1.0.0 -> 1000000], [1.2.3 -> 1002003], [0.6.1 -> 6001]
  QString OVS = Lumina::SETTINGS->value(DesktopSettings::System,"DesktopVersion","0").toString(); //Old Version String
  bool changed = LDesktopUtils::checkUserFiles(OVS);
  if(changed){
    //Save the current version of the session to the settings file (for next time)
    Lumina::SETTINGS->setValue(DesktopSettings::System,"DesktopVersion", this->applicationVersion());
  }
}


//==================
//  PUBLIC SLOTS
//==================
void LSession::StartLogout(){
  CleanupSession();
  QCoreApplication::exit(0);
}

void LSession::StartShutdown(bool skipupdates){
  CleanupSession();
  LOS::systemShutdown(skipupdates);
  QCoreApplication::exit(0);
}

void LSession::StartReboot(bool skipupdates){
  CleanupSession();
  LOS::systemRestart(skipupdates);
  QCoreApplication::exit(0);
}

void LSession::reloadIconTheme(){
  //Wait a moment for things to settle before sending out the signal to the interfaces
  QApplication::processEvents();
  QApplication::processEvents();
  emit IconThemeChanged();
}

//Temporarily change the session locale (nothing saved between sessions)
void LSession::switchLocale(QString localeCode){
  currTranslator = LUtils::LoadTranslation(this, "lumina-desktop", localeCode, currTranslator);
  if(currTranslator!=0 || localeCode=="en_US"){
    LUtils::setLocaleEnv(localeCode); //will set everything to this locale (no custom settings)
  }
  emit LocaleChanged();
}
bgstack15