1 //===========================================
2 // Lumina desktop source code
3 // Copyright (c) 2017, Ken Moore
4 // Available under the 3-clause BSD license
5 // See the LICENSE file for full details
6 //===========================================
7 // Internal, OS-agnostic functionality for managing the object itself
8 //===========================================
9 #include <framework-OSInterface.h>
10 #include <QtConcurrent>
11 #include <QIcon>
12 #include <QQmlEngine>
13
OSInterface(QObject * parent)14 OSInterface::OSInterface(QObject *parent) : QObject(parent){
15 watcher = 0;
16 iodevice = 0;
17 netman = 0;
18 }
19
~OSInterface()20 OSInterface::~OSInterface(){
21 if(watcher!=0){
22 QStringList paths; paths << watcher->files() << watcher->directories();
23 if(!paths.isEmpty()){ watcher->removePaths(paths); }
24 watcher->deleteLater();
25 }
26 if(iodevice!=0){
27 if(iodevice->isOpen()){ iodevice->close(); }
28 iodevice->deleteLater();
29 }
30 if(netman!=0){
31 netman->deleteLater();
32 }
33 }
34
instance()35 OSInterface* OSInterface::instance(){
36 static OSInterface* m_os_object = 0;
37 if(m_os_object==0){
38 m_os_object = new OSInterface();
39 }
40 return m_os_object;
41 }
42
RegisterType()43 void OSInterface::RegisterType(){
44 static bool done = false;
45 if(done){ return; }
46 done=true;
47 qmlRegisterType<OSInterface>("Lumina.Backend.OSInterface", 2, 0, "OSInterface");
48 }
49
50 //Start/stop interface systems
start()51 void OSInterface::start(){
52 if(!mediaDirectories().isEmpty()){ setupMediaWatcher(); }//will create/connect the filesystem watcher automatically
53 setupNetworkManager(60000, 1); //will create/connect the network monitor automatically
54 if(batteryAvailable()){ setupBatteryMonitor(30000, 1); } //30 second updates, 1 ms init delay
55 if(brightnessSupported()){ setupBrightnessMonitor(60000, 1); } //1 minute updates, 1 ms init delay
56 if(volumeSupported()){ setupVolumeMonitor(60000, 2); } //1 minute updates, 2 ms init delay
57 if(updatesSupported()){ setupUpdateMonitor(12*60*60*1000, 5*60*1000); } //12-hour updates, 5 minute delay
58 if(cpuSupported()){ setupCpuMonitor(2000, 20); } //2 second updates, 20 ms init delay
59 if(memorySupported()){ setupMemoryMonitor(2000, 21); } //2 second updates, 21 ms init delay
60 if(diskSupported()){ setupDiskMonitor(60000, 25); } //1 minute updates, 25 ms init delay
61 }
62
stop()63 void OSInterface::stop(){
64 if(watcher!=0){ watcher->deleteLater(); watcher=0; }
65 if(batteryTimer!=0){ batteryTimer->stop(); disconnect(batteryTimer); }
66 if(brightnessTimer!=0){ brightnessTimer->stop(); disconnect(brightnessTimer); }
67 if(volumeTimer!=0){ volumeTimer->stop(); disconnect(volumeTimer); }
68 if(updateTimer!=0){ updateTimer->stop(); disconnect(updateTimer); }
69 if(cpuTimer!=0){ cpuTimer->stop(); disconnect(cpuTimer); }
70 if(memTimer!=0){ memTimer->stop(); disconnect(memTimer); }
71 if(diskTimer!=0){ diskTimer->stop(); disconnect(diskTimer); }
72 if(netman!=0){ disconnect(netman); netman->deleteLater(); netman = 0; }
73 }
74
isRunning()75 bool OSInterface::isRunning(){ return _started; } //status of the object - whether it has been started yet
76
connectWatcher()77 void OSInterface::connectWatcher(){
78 if(watcher==0){ return; }
79 connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherFileChanged(QString)) );
80 connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherDirChanged(QString)) );
81 }
82
connectIodevice()83 void OSInterface::connectIodevice(){
84 if(iodevice==0){ return; }
85 connect(iodevice, SIGNAL(readyRead()), this, SLOT(iodeviceReadyRead()) );
86 }
87
connectNetman()88 void OSInterface::connectNetman(){
89 if(netman==0){ return; }
90 connect(netman, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(NetworkTimerUpdate()) );
91 connect(netman, SIGNAL(finished(QNetworkReply*)), this, SLOT(netRequestFinished(QNetworkReply*)) );
92 connect(netman, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(netSslErrors(QNetworkReply*, const QList<QSslError>&)) );
93 }
94
verifyAppOrBin(QString chk)95 bool OSInterface::verifyAppOrBin(QString chk){
96 bool valid = !chk.isEmpty();
97 if(chk.contains(" ")){ chk = chk.section(" ",0,0); }
98 if(valid && chk.endsWith(".desktop")){
99 if(chk.startsWith("/")){ return QFile::exists(chk); }
100 valid = false;
101 QStringList paths;
102 paths << QString(getenv("XDG_DATA_HOME")) << QString(getenv("XDG_DATA_DIRS")).split(":");
103 for(int i=0; i<paths.length() && !valid; i++){
104 if(QFile::exists(paths[i]+"/applications")){ valid = findInDirectory(chk, paths[i]+"/applications", true); }
105 }
106 }else if(valid){
107 //Find the absolute path for this binary
108 if(!chk.startsWith("/")){
109 QStringList paths = QString(getenv("PATH")).split(":");
110 for(int i=0; i<paths.length(); i++){
111 if(QFile::exists(paths[i]+"/"+chk)){ chk = paths[i]+"/"+chk; break; }
112 }
113 if(!chk.startsWith("/")){ return false; } //could not find the file
114 }else if(!QFile::exists(chk)){
115 return false; //file does not exist
116 }
117 //Make sure it is executable by the user
118 valid = QFileInfo(chk).isExecutable();
119 }
120 return valid;
121 }
122
runProcess(int & retcode,QString command,QStringList arguments,QString workdir,QStringList env)123 QString OSInterface::runProcess(int &retcode, QString command, QStringList arguments, QString workdir, QStringList env){
124 QProcess proc;
125 proc.setProcessChannelMode(QProcess::MergedChannels); //need output
126 //First setup the process environment as necessary
127 QProcessEnvironment PE = QProcessEnvironment::systemEnvironment();
128 if(!env.isEmpty()){
129 for(int i=0; i<env.length(); i++){
130 if(!env[i].contains("=")){ continue; }
131 PE.insert(env[i].section("=",0,0), env[i].section("=",1,100));
132 }
133 }
134 proc.setProcessEnvironment(PE);
135 //if a working directory is specified, check it and use it
136 if(!workdir.isEmpty()){
137 proc.setWorkingDirectory(workdir);
138 }
139 //Now run the command (with any optional arguments)
140 if(arguments.isEmpty()){ proc.start(command); }
141 else{ proc.start(command, arguments); }
142 //Wait for the process to finish (but don't block the event loop)
143 for(int i=0; i<10 && !proc.waitForFinished(500); i++){ //maximum of 5 seconds for command to finish
144 if(proc.state() == QProcess::NotRunning){ break; } //somehow missed the finished signal - go ahead and stop now
145 }
146 if(proc.state() != QProcess::NotRunning){ proc.terminate(); } //just in case - make sure to kill off the process
147 QString info = proc.readAllStandardOutput();
148 retcode = proc.exitCode(); //return success/failure
149 return info;
150 }
151
runCmd(QString command,QStringList args)152 int OSInterface::runCmd(QString command, QStringList args){
153 int retcode;
154 runProcess(retcode, command, args);
155 return retcode;
156 }
157
getCmdOutput(QString command,QStringList args)158 QStringList OSInterface::getCmdOutput(QString command, QStringList args){
159 int retcode;
160 return runProcess(retcode, command, args).split("\n");
161 }
162
findInDirectory(QString file,QString dirpath,bool recursive)163 bool OSInterface::findInDirectory(QString file, QString dirpath, bool recursive){
164 bool found = QFile::exists(dirpath+"/"+file);
165 if(!found && recursive){
166 QDir dir(dirpath);
167 QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
168 for(int i=0; i<dirs.length() && !found; i++){ found = findInDirectory(file, dir.absoluteFilePath(dirs[i]), recursive); }
169 }
170 return found;
171 }
172
readFile(QString path)173 QString OSInterface::readFile(QString path){
174 QFile file(path);
175 QString info;
176 if(file.open(QIODevice::ReadOnly)){
177 QTextStream out(&file);
178 info = out.readAll();
179 file.close();
180 }
181 return info;
182 }
183
184 // ===========================
185 // OS SPECIFIC EXISTANCE CHECKS
186 // ===========================
hasControlPanel()187 bool OSInterface::hasControlPanel(){
188 return verifyAppOrBin(controlPanelShortcut());
189 }
190
hasAudioMixer()191 bool OSInterface::hasAudioMixer(){
192 return verifyAppOrBin(audioMixerShortcut());
193 }
194
hasAppStore()195 bool OSInterface::hasAppStore(){
196 return verifyAppOrBin(appStoreShortcut());
197 }
198
199 // ========================
200 // MEDIA DIRECTORIES
201 // ========================
202
203 // External Media Management (if system uses *.desktop shortcuts)
setupMediaWatcher()204 void OSInterface::setupMediaWatcher(){
205 //Create/connect the watcher if needed
206 if(watcher == 0){ watcher = new QFileSystemWatcher(); connectWatcher(); }
207 QStringList dirs = this->mediaDirectories();
208 if(dirs.isEmpty()){ return; } //nothing to do
209 //Make sure each directory is scanned **right now** (if it exists)
210 for(int i=0; i<dirs.length(); i++){
211 if(QFile::exists(dirs[i])){
212 handleMediaDirChange(dirs[i]);
213 }
214 }
215 }
216
handleMediaDirChange(QString dir)217 bool OSInterface::handleMediaDirChange(QString dir){ //returns true if directory was handled
218 if( !this->mediaDirectories().contains(dir) ){ return false; } //not a media directory
219 QDir qdir(dir);
220 QStringList files = qdir.entryList(QStringList() << "*.desktop", QDir::Files, QDir::Name);
221 for(int i=0; i<files.length(); i++){ files[i] = qdir.absoluteFilePath(files[i]); }
222 QString key = "media_files/"+dir;
223 if(files.isEmpty() && INFO.contains(key)){ INFO.remove(key); emit mediaShortcutsChanged(); } //no files for this directory at the moment
224 else{ INFO.insert("media_files/"+dir, files); emit mediaShortcutsChanged(); } //save these file paths for later
225 //Make sure the directory is still watched (sometimes the dir is removed/recreated on modification)
226 if(!watcher->directories().contains(dir)){ watcher->addPath(dir); }
227 return true;
228 }
229
autoHandledMediaFiles()230 QStringList OSInterface::autoHandledMediaFiles(){
231 QStringList files;
232 QStringList keys = INFO.keys().filter("media_files/");
233 for(int i=0; i<keys.length(); i++){
234 if(keys[i].startsWith("media_files/")){ files << INFO[keys[i]].toStringList(); }
235 }
236 return files;
237 }
238
239 // =============================
240 // NETWORK INTERFACE FUNCTIONS
241 // =============================
242 // Qt-based NetworkAccessManager usage
setupNetworkManager(int update_ms,int delay_ms)243 void OSInterface::setupNetworkManager(int update_ms, int delay_ms){
244 if(netman==0){
245 netman = new QNetworkAccessManager(this);
246 connectNetman();
247 }
248 networkTimer = new QTimer(this);
249 networkTimer->setSingleShot(true);
250 networkTimer->setInterval(update_ms);
251 connect(networkTimer, SIGNAL(timeout()), this, SLOT(NetworkTimerUpdate()) );
252 QTimer::singleShot(delay_ms, this, SLOT(NetworkTimerUpdate()) );
253 }
254
networkAvailable()255 bool OSInterface::networkAvailable(){
256 if(INFO.contains("netaccess/available")){ return INFO.value("netaccess/available").toBool(); }
257 return false;
258 }
259
networkType()260 QString OSInterface::networkType(){
261 if(INFO.contains("netaccess/type")){ return INFO.value("netaccess/type").toString(); } //"wifi", "wired", or "cell"
262 return "";
263 }
264
networkStrength()265 float OSInterface::networkStrength(){
266 if(INFO.contains("netaccess/strength")){ return INFO.value("netaccess/strength").toFloat(); } //percentage
267 return -1;
268 }
269
networkIcon()270 QString OSInterface::networkIcon(){
271 if(INFO.contains("netaccess/icon")){ return INFO.value("netaccess/icon").toString(); }
272 return "";
273 }
274
networkHostname()275 QString OSInterface::networkHostname(){
276 return QHostInfo::localHostName();
277 }
278
networkAddress()279 QStringList OSInterface::networkAddress(){
280 QString addr;
281 if(INFO.contains("netaccess/address")){ addr = INFO.value("netaccess/address").toString(); }
282 return addr.split(", ");
283 }
284
hasNetworkManager()285 bool OSInterface::hasNetworkManager(){
286 return verifyAppOrBin(networkManagerUtility());
287 }
288
networkStatus()289 QString OSInterface::networkStatus(){
290 QString stat = "<b>%1</b><br>%2<br>%3";
291 return stat.arg(networkHostname(), networkType(), networkAddress().join("<br>"));
292 }
293
294 //NetworkAccessManager slots
syncNetworkInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)295 void OSInterface::syncNetworkInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
296 //qDebug() << "[DEBUG] Got Net Access Changed";
297 hash->insert("netaccess/available", netman->networkAccessible()== QNetworkAccessManager::Accessible);
298 //Update all the other network status info at the same time
299 QNetworkConfiguration active;
300 QList<QNetworkConfiguration> netconfigL = netman->configuration().children();
301 for(int i=0; i<netconfigL.length(); i++){
302 if(!netconfigL[i].state().testFlag(QNetworkConfiguration::Discovered) ){ continue; } //skip this interface
303 QList<QNetworkAddressEntry> addressList = QNetworkInterface::interfaceFromName(netconfigL[i].name()).addressEntries();
304 //NOTE: There are often 2 addresses, IPv4 and IPv6
305 bool ok = false;
306 for(int j=0; j<addressList.length() && !ok; j++){
307 if( addressList[j].ip().isLoopback() ){ continue; }
308 else if(addressList[i].ip().isEqual(QHostAddress(QHostAddress::LocalHost)) || addressList[i].ip().isEqual(QHostAddress::LocalHostIPv6) ){ continue; }
309 addressList[j].ip().toIPv4Address(&ok);
310 }
311 if(ok){ active = netconfigL[i]; break; } //found a good one with a valid IPv4
312 //else if(!active.isValid()){
313 }
314 if(!active.isValid()){ active = netman->activeConfiguration(); } //use the default Qt-detected interface
315 //Type of connection
316 QString type;
317 switch(active.bearerTypeFamily()){
318 case QNetworkConfiguration::BearerEthernet: type="wired"; break;
319 case QNetworkConfiguration::BearerWLAN: type="wifi"; break;
320 case QNetworkConfiguration::Bearer2G: type="cell-2G"; break;
321 case QNetworkConfiguration::Bearer3G: type="cell-3G"; break;
322 case QNetworkConfiguration::Bearer4G: type="cell-4G"; break;
323 default: type=OS_networkTypeFromDeviceName(active.name()); //could not be auto-determined - run the OS-specific routine
324 }
325 hash->insert("netaccess/type", type);
326 float strength = 100;
327 if(type!="wired"){ strength = OS_networkStrengthFromDeviceName(active.name()); }
328 hash->insert("netaccess/strength", strength);
329
330 //qDebug() << "Detected Device Status:" << active.identifier() << type << stat;
331 QNetworkInterface iface = QNetworkInterface::interfaceFromName(active.name());
332 //qDebug() << " - Configuration: Name:" << active.name() << active.bearerTypeName() << active.identifier();
333 //qDebug() << " - Interface: MAC Address:" << iface.hardwareAddress() << "Name:" << iface.name() << iface.humanReadableName() << iface.isValid();
334 QList<QNetworkAddressEntry> addressList = iface.addressEntries();
335 QStringList address;
336 //NOTE: There are often 2 addresses, IPv4 and IPv6
337 for(int i=0; i<addressList.length(); i++){
338 address << addressList[i].ip().toString();
339 }
340 //qDebug() << " - IP Address:" << address;
341 //qDebug() << " - Hostname:" << networkHostname();
342 hash->insert("netaccess/address", address.join(", "));
343
344 //Figure out the icon used for this type/strnegth
345 QStringList icons;
346 if(type.startsWith("cell")){
347 if(address.isEmpty()){ icons <<"network-cell-off"; }
348 else if(strength>80){ icons <<"network-cell-connected-100"; }
349 else if(strength>60){ icons <<"network-cell-connected-75"; }
350 else if(strength>40){ icons << "network-cell-connected-50"; }
351 else if(strength>10){ icons << "network-cell-connected-25"; }
352 else if(strength >=0){ icons << "network-cell-connected-00"; }
353 icons << "network-cell"; //fallback - just use generic icon so we at least get off/on visibility
354
355 }else if(type=="wifi"){
356 if(address.isEmpty()){ icons << "network-wireless-disconnected" << "network-wireless-00" << "network-wireless-off"; }
357 else if(strength>80){ icons << "network-wireless-100"; }
358 else if(strength>60){ icons << "network-wireless-75"; }
359 else if(strength>40){ icons << "network-wireless-50"; }
360 else if(strength>10){ icons << "network-wireless-25"; }
361 else if(strength >=0){ icons << "network-wireless-00"; }
362 icons << "network-wireless"; //fallback - just use generic icon so we at least get off/on visibility
363
364 }else if(type=="wired"){
365 if(strength==100 && !address.isEmpty()){ icons << "network-wired-connected" << "network-wired"; }
366 else if(strength==100){ icons << "network-wired-pending" << "network-wired-aquiring"; }
367 else{ icons << "network-wired-unavailable" << "network-wired-disconnected" ; }
368 }
369 icons << "network-workgroup" << "network-unknown";
370 for(int i=0; i<icons.length(); i++){
371 if(QIcon::hasThemeIcon(icons[i])){ hash->insert("netaccess/icon",icons[i]); break; }
372 }
373 //qDebug() << "[DEBUG] Emit NetworkStatusChanged";
374 os->emit networkStatusChanged();
375 QTimer::singleShot(0, timer, SLOT(start()));
376 }
377
378
379 // ========================
380 // TIMER-BASED MONITORS
381 // ========================
382 //Timer slots
383
NetworkTimerUpdate()384 void OSInterface::NetworkTimerUpdate(){
385 if(networkTimer->isActive()){ networkTimer->stop(); } //just in case this was manually triggered
386 QtConcurrent::run(this, &OSInterface::syncNetworkInfo, this, &INFO, networkTimer);
387 }
388
BatteryTimerUpdate()389 void OSInterface::BatteryTimerUpdate(){
390 if(batteryTimer->isActive()){ batteryTimer->stop(); } //just in case this was manually triggered
391 QtConcurrent::run(this, &OSInterface::syncBatteryInfo, this, &INFO, batteryTimer);
392 }
393
UpdateTimerUpdate()394 void OSInterface::UpdateTimerUpdate(){
395 if(updateTimer->isActive()){ updateTimer->stop(); } //just in case this was manually triggered
396 QtConcurrent::run(this, &OSInterface::syncUpdateInfo, this, &INFO, updateTimer);
397 }
398
BrightnessTimerUpdate()399 void OSInterface::BrightnessTimerUpdate(){
400 if(brightnessTimer->isActive()){ brightnessTimer->stop(); } //just in case this was manually triggered
401 QtConcurrent::run(this, &OSInterface::syncBrightnessInfo, this, &INFO, brightnessTimer);
402 }
403
VolumeTimerUpdate()404 void OSInterface::VolumeTimerUpdate(){
405 if(volumeTimer->isActive()){ volumeTimer->stop(); } //just in case this was manually triggered
406 QtConcurrent::run(this, &OSInterface::syncVolumeInfo, this, &INFO, volumeTimer);
407 }
408
CpuTimerUpdate()409 void OSInterface::CpuTimerUpdate(){
410 if(cpuTimer->isActive()){ cpuTimer->stop(); } //just in case this was manually triggered
411 QtConcurrent::run(this, &OSInterface::syncCpuInfo, this, &INFO, cpuTimer);
412 }
413
MemTimerUpdate()414 void OSInterface::MemTimerUpdate(){
415 if(memTimer->isActive()){ memTimer->stop(); } //just in case this was manually triggered
416 QtConcurrent::run(this, &OSInterface::syncMemoryInfo, this, &INFO, memTimer);
417 }
418
DiskTimerUpdate()419 void OSInterface::DiskTimerUpdate(){
420 if(diskTimer->isActive()){ diskTimer->stop(); } //just in case this was manually triggered
421 QtConcurrent::run(this, &OSInterface::syncDiskInfo, this, &INFO, diskTimer);
422 }
423
424 // Timer Setup functions
setupBatteryMonitor(int update_ms,int delay_ms)425 void OSInterface::setupBatteryMonitor(int update_ms, int delay_ms){
426 batteryTimer = new QTimer(this);
427 batteryTimer->setSingleShot(true);
428 batteryTimer->setInterval(update_ms);
429 connect(batteryTimer, SIGNAL(timeout()), this, SLOT(BatteryTimerUpdate()) );
430 QTimer::singleShot(delay_ms, this, SLOT(BatteryTimerUpdate()) );
431 }
setupUpdateMonitor(int update_ms,int delay_ms)432 void OSInterface::setupUpdateMonitor(int update_ms, int delay_ms){
433 updateTimer = new QTimer(this);
434 updateTimer->setSingleShot(true);
435 updateTimer->setInterval(update_ms);
436 connect(updateTimer, SIGNAL(timeout()), this, SLOT(UpdateTimerUpdate()) );
437 QTimer::singleShot(delay_ms, this, SLOT(UpdateTimerUpdate()) );
438 }
setupBrightnessMonitor(int update_ms,int delay_ms)439 void OSInterface::setupBrightnessMonitor(int update_ms, int delay_ms){
440 brightnessTimer = new QTimer(this);
441 brightnessTimer->setSingleShot(true);
442 brightnessTimer->setInterval(update_ms);
443 connect(brightnessTimer, SIGNAL(timeout()), this, SLOT(BrightnessTimerUpdate()) );
444 QTimer::singleShot(delay_ms, this, SLOT(BrightnessTimerUpdate()) );
445 }
setupVolumeMonitor(int update_ms,int delay_ms)446 void OSInterface::setupVolumeMonitor(int update_ms, int delay_ms){
447 volumeTimer = new QTimer(this);
448 volumeTimer->setSingleShot(true);
449 volumeTimer->setInterval(update_ms);
450 connect(volumeTimer, SIGNAL(timeout()), this, SLOT(VolumeTimerUpdate()) );
451 QTimer::singleShot(delay_ms, this, SLOT(VolumeTimerUpdate()) );
452 }
setupCpuMonitor(int update_ms,int delay_ms)453 void OSInterface::setupCpuMonitor(int update_ms, int delay_ms){
454 cpuTimer = new QTimer(this);
455 cpuTimer->setSingleShot(true);
456 cpuTimer->setInterval(update_ms);
457 connect(cpuTimer, SIGNAL(timeout()), this, SLOT(CpuTimerUpdate()) );
458 QTimer::singleShot(delay_ms, this, SLOT(CpuTimerUpdate()) );
459 }
setupMemoryMonitor(int update_ms,int delay_ms)460 void OSInterface::setupMemoryMonitor(int update_ms, int delay_ms){
461 memTimer = new QTimer(this);
462 memTimer->setSingleShot(true);
463 memTimer->setInterval(update_ms);
464 connect(memTimer, SIGNAL(timeout()), this, SLOT(MemTimerUpdate()) );
465 QTimer::singleShot(delay_ms, this, SLOT(MemTimerUpdate()) );
466 }
setupDiskMonitor(int update_ms,int delay_ms)467 void OSInterface::setupDiskMonitor(int update_ms, int delay_ms){
468 diskTimer = new QTimer(this);
469 diskTimer->setSingleShot(true);
470 diskTimer->setInterval(update_ms);
471 connect(diskTimer, SIGNAL(timeout()), this, SLOT(DiskTimerUpdate()) );
472 QTimer::singleShot(delay_ms, this, SLOT(DiskTimerUpdate()) );
473 }
474
475 // Timer-based monitor update routines (NOTE: these are all run in a separate thread!!)
syncBatteryInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)476 void OSInterface::syncBatteryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
477 float charge = OS_batteryCharge();
478 bool charging = OS_batteryCharging();
479 double secs = OS_batterySecondsLeft();
480 //Check for any alert generations
481 if(charging && hash->value("battery/percent",100).toFloat() <= 99 && charge>99){ os->emit BatteryFullAlert(); }
482 else if(!charging && hash->value("battery/percent", 50).toFloat()>10 && charge<10){ os->emit BatteryEmptyAlert(); }
483
484 hash->insert("battery/percent",charge);
485 hash->insert("battery/charging",charging);
486 //Convert the seconds to human-readable
487 QString time;
488 if(secs>3600){
489 time = QString::number( qRound(secs/360.0)/10.0 )+" h";
490 }else if(secs>60){
491 time = QString::number( qRound(secs/6.0)/10.0 )+" m";
492 }else if(secs>0){
493 time = QString::number(secs)+" s";
494 }
495 hash->insert("battery/time", time);
496 //Determine the icon which should be used for this status
497 QStringList icons;
498 if(charging){
499 if(charge>=99){ icons << "battery-100-charging" << "battery-charging-100" << "battery-full-charging" << "battery-charging-full" << "battery-charging"; }
500 else if(charge >80){ icons << "battery-charging-80"<< "battery-80-charging"<< "battery-charging-080" << "battery-080-charging" << "battery-good-charging" << "battery-charging-good"; }
501 else if(charge >60){ icons << "battery-charging-60"<< "battery-60-charging"<< "battery-charging-060" << "battery-060-charging" << "battery-good-charging" << "battery-charging-good"; }
502 else if(charge >40){ icons << "battery-charging-40"<< "battery-40-charging"<< "battery-charging-040" << "battery-040-charging" << "battery-good-charging" << "battery-charging-good"; }
503 else if(charge >20){ icons << "battery-charging-20"<< "battery-20-charging"<< "battery-charging-020" << "battery-020-charging" << "battery-low-charging" << "battery-charging-low"; }
504 else if(charge > 0){ icons << "battery-charging-00"<< "battery-00-charging"<< "battery-charging-000" << "battery-000-charging" << "battery-caution-charging" << "battery-charging-caution"; }
505 }else{
506 if(charge>=99){ icons << "battery-100" << "battery-full-charged" << "battery-full" << "battery"; }
507 else if(charge >80){ icons << "battery-80"<< "battery-080" << "battery-good"; }
508 else if(charge >60){ icons << "battery-60"<< "battery-060" << "battery-good"; }
509 else if(charge >40){ icons << "battery-40"<< "battery-040" << "battery-good"; }
510 else if(charge >20){ icons << "battery-20"<< "battery-020" << "battery-low"; }
511 else if(charge > 0){ icons << "battery-00" << "battery-000" << "battery-caution"; }
512 }
513 icons << "battery-unknown" << "battery";
514 for(int i=0; i<icons.length(); i++){
515 if(QIcon::hasThemeIcon(icons[i])){ hash->insert("battery/icon",icons[i]); break; }
516 }
517 //Now emit the change signal and restart the timer
518 os->emit batteryChanged();
519 QTimer::singleShot(0, timer, SLOT(start()));
520 }
521
syncUpdateInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)522 void OSInterface::syncUpdateInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
523 //Get the current status
524 QString status;
525 QStringList icons;
526 if(OS_updatesRunning()){
527 status="running"; icons << "state-download" << "update-medium" << "sync";
528 }else if(OS_updatesFinished()){
529 status="finished"; icons << "state-ok" << "update-high" << "security-high";
530 }else if(OS_updatesAvailable()){
531 status="available"; icons << "state-warning" << "update-medium" << "security-medium";
532 }
533 icons << "state-offline" << "update-none";
534 //qDebug() << "Update Sync:" << status << icons;
535 //Save the current info into the hash (if different)
536 if(status != updateStatus()){
537 hash->insert("updates/status", status);
538 for(int i=0; i<icons.length(); i++){
539 if(QIcon::hasThemeIcon(icons[i])){ hash->insert("updates/icon", icons[i]); break;}
540 }
541 os->emit updateStatusChanged();
542 }
543 QTimer::singleShot(0, timer, SLOT(start()));
544 }
545
syncBrightnessInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)546 void OSInterface::syncBrightnessInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
547
548 QTimer::singleShot(0, timer, SLOT(start()));
549 }
550
syncVolumeInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)551 void OSInterface::syncVolumeInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
552 int oldvol = volume();
553 int newvol = OS_volume();
554 if(oldvol!=newvol && newvol>=0){
555 hash->insert("volume/current",newvol);
556 QString icon;
557 if(newvol>66){ icon = "audio-volume-high"; }
558 else if(newvol>33){ icon = "audio-volume-medium"; }
559 else if(newvol>0){ icon = "audio-volume-low"; }
560 else{ icon = "audio-volume-muted"; }
561 hash->insert("volume/icon",icon);
562 os->emit volumeChanged();
563 }
564 QTimer::singleShot(0, timer, SLOT(start()));
565 }
566
syncCpuInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)567 void OSInterface::syncCpuInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
568
569 QTimer::singleShot(0, timer, SLOT(start()));
570 }
571
syncMemoryInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)572 void OSInterface::syncMemoryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
573
574 QTimer::singleShot(0, timer, SLOT(start()));
575 }
576
syncDiskInfo(OSInterface * os,QHash<QString,QVariant> * hash,QTimer * timer)577 void OSInterface::syncDiskInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){
578
579 QTimer::singleShot(0, timer, SLOT(start()));
580 }
581
582 // = Battery =
batteryAvailable()583 bool OSInterface::batteryAvailable(){ return OS_batteryAvailable(); }
batteryCharge()584 float OSInterface::batteryCharge(){
585 if(INFO.contains("battery/percent")){ return INFO.value("battery/percent").toFloat(); }
586 return -1;
587 }
batteryCharging()588 bool OSInterface::batteryCharging(){
589 if(INFO.contains("battery/charging")){ return INFO.value("battery/charging").toBool(); }
590 return false;
591 }
batteryRemaining()592 QString OSInterface::batteryRemaining(){
593 if(INFO.contains("battery/time")){ return INFO.value("battery/time").toString(); }
594 return "";
595 }
batteryIcon()596 QString OSInterface::batteryIcon(){
597 if(INFO.contains("battery/icon")){ return INFO.value("battery/icon").toString(); }
598 return "";
599 }
600
batteryStatus()601 QString OSInterface::batteryStatus(){
602 QString text = QString::number(batteryCharge())+"%";
603 if(!batteryCharging()){
604 QString time = batteryRemaining();
605 if(!time.isEmpty()){
606 text.append(" ("+time+")");
607 }
608 }
609 return text;
610 }
611
612 // = Volume =
volumeSupported()613 bool OSInterface::volumeSupported(){ return OS_volumeSupported(); }
volume()614 int OSInterface::volume(){
615 if(INFO.contains("volume/current")){ return INFO.value("volume/current").toInt(); }
616 return 0;
617 }
618
setVolume(int vol)619 void OSInterface::setVolume(int vol){
620 OS_setVolume(vol);
621 VolumeTimerUpdate(); //update the internal cache
622 }
623
volumeIcon()624 QString OSInterface::volumeIcon(){
625 if(INFO.contains("volume/icon")){ return INFO.value("volume/icon").toString(); }
626 return "";
627 }
628
629 // = Media =
mediaDirectories()630 QStringList OSInterface::mediaDirectories(){ return OS_mediaDirectories(); }
mediaShortcuts()631 QStringList OSInterface::mediaShortcuts(){ return autoHandledMediaFiles(); } //List of currently-available XDG shortcut file paths
632
633 // = Updates =
updatesSupported()634 bool OSInterface::updatesSupported(){ return OS_updatesSupported(); }
updateStatus()635 QString OSInterface::updateStatus(){
636 if(INFO.contains("updates/status")){ return INFO.value("updates/status").toString(); }
637 return "";
638 }
updateInfoAvailable()639 bool OSInterface::updateInfoAvailable(){
640 return !updateStatus().isEmpty();
641 }
642
updateIcon()643 QString OSInterface::updateIcon(){
644 if(INFO.contains("updates/icon")){ return INFO.value("updates/icon").toString(); }
645 return "";
646 }
647
updateStatusInfo()648 QString OSInterface::updateStatusInfo(){
649 QString status = updateStatus();
650 if(status=="available"){ return updateDetails(); }
651 else if(status=="running"){ return updateLog(); }
652 else if(status=="finished"){ return updateResults(); }
653 return "";
654 }
655
updateDetails()656 QString OSInterface::updateDetails(){
657 return OS_updateDetails(); //don't cache these types of logs - too large
658 }
659
updateLog()660 QString OSInterface::updateLog(){
661 return OS_updateLog(); //don't cache these types of logs - too large and change too often
662 }
663
updateResults()664 QString OSInterface::updateResults(){
665 return OS_updateResults(); //don't cache these types of logs - too large
666 }
667
startUpdates()668 void OSInterface::startUpdates(){ OS_startUpdates(); }
updateOnlyOnReboot()669 bool OSInterface::updateOnlyOnReboot(){ return OS_updateOnlyOnReboot(); }
updateCausesReboot()670 bool OSInterface::updateCausesReboot(){ return OS_updateCausesReboot(); }
671
lastUpdate()672 QDateTime OSInterface::lastUpdate(){ return OS_lastUpdate(); }
lastUpdateResults()673 QString OSInterface::lastUpdateResults(){ return OS_lastUpdateResults(); }
674
675 // = System Power =
canReboot()676 bool OSInterface::canReboot(){ return OS_canReboot(); }
startReboot()677 void OSInterface::startReboot(){ OS_startReboot(); }
canShutdown()678 bool OSInterface::canShutdown(){ return OS_canShutdown(); }
startShutdown()679 void OSInterface::startShutdown(){ OS_startShutdown(); }
canSuspend()680 bool OSInterface::canSuspend(){ return OS_canSuspend(); }
startSuspend()681 void OSInterface::startSuspend(){ OS_startSuspend(); }
682
683 // = Screen Brightness =
brightnessSupported()684 bool OSInterface::brightnessSupported(){ return OS_brightnessSupported(); }
brightness()685 int OSInterface::brightness(){
686 if(INFO.contains("brightness/percent")){ return INFO.value("brightness/percent").toInt(); }
687 return 100;
688 }
setBrightness(int percent)689 void OSInterface::setBrightness(int percent){
690 OS_setBrightness(percent);
691 BrightnessTimerUpdate(); //update internal cache ASAP
692 }
693
694 // = System Status Monitoring
cpuSupported()695 bool OSInterface::cpuSupported(){ return OS_cpuSupported(); }
cpuPercentage()696 QList<int> OSInterface::cpuPercentage(){ return QList<int>(); } // (one per CPU) percentage: 0-100 with empty list for errors
cpuTemperatures()697 QStringList OSInterface::cpuTemperatures(){ return QStringList(); } // (one per CPU) Temperature of CPU ("50C" for example)
698
memorySupported()699 bool OSInterface::memorySupported(){ return false; }
memoryUsedPercentage()700 int OSInterface::memoryUsedPercentage(){ return -1; } //percentage: 0-100 with -1 for errors
memoryTotal()701 QString OSInterface::memoryTotal(){ return QString(); } //human-readable form - does not tend to change within a session
diskIO()702 QStringList OSInterface::diskIO(){ return QStringList(); } //Returns list of current read/write stats for each device
703
diskSupported()704 bool OSInterface::diskSupported(){ return false; }
fileSystemPercentage(QString dir)705 int OSInterface::fileSystemPercentage(QString dir){ return -1; } //percentage of capacity used: 0-100 with -1 for errors
fileSystemCapacity(QString dir)706 QString OSInterface::fileSystemCapacity(QString dir){ return QString(); } //human-readable form - total capacity
707