aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/libLumina
diff options
context:
space:
mode:
authorKen Moore <moorekou@gmail.com>2016-04-25 13:08:12 -0400
committerKen Moore <moorekou@gmail.com>2016-04-25 13:08:12 -0400
commited5ecf7ea7a482b4649e66ecb35fbc60af680684 (patch)
treeacc0fa17d228259e847f55c678db9fb0a9b50f0c /src-qt5/core/libLumina
parentMerge branch 'master' of github.com:pcbsd/lumina (diff)
downloadlumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.gz
lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.bz2
lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.zip
Rearrange the Lumina source tree quite a bit:
Now the utilites are arranged by category (core, core-utils, desktop-utils), so all the -utils may be excluded by a package system (or turned into separate packages) as needed.
Diffstat (limited to 'src-qt5/core/libLumina')
-rw-r--r--src-qt5/core/libLumina/LuminaOS-Debian.cpp300
-rw-r--r--src-qt5/core/libLumina/LuminaOS-DragonFly.cpp355
-rw-r--r--src-qt5/core/libLumina/LuminaOS-FreeBSD.cpp362
-rw-r--r--src-qt5/core/libLumina/LuminaOS-Gentoo.cpp300
-rw-r--r--src-qt5/core/libLumina/LuminaOS-Linux.cpp245
-rw-r--r--src-qt5/core/libLumina/LuminaOS-NetBSD.cpp169
-rw-r--r--src-qt5/core/libLumina/LuminaOS-OpenBSD.cpp257
-rw-r--r--src-qt5/core/libLumina/LuminaOS-kFreeBSD.cpp198
-rw-r--r--src-qt5/core/libLumina/LuminaOS-template.cpp145
-rw-r--r--src-qt5/core/libLumina/LuminaOS.h93
-rw-r--r--src-qt5/core/libLumina/LuminaSingleApplication.cpp137
-rw-r--r--src-qt5/core/libLumina/LuminaSingleApplication.h59
-rw-r--r--src-qt5/core/libLumina/LuminaThemes.cpp503
-rw-r--r--src-qt5/core/libLumina/LuminaThemes.h115
-rw-r--r--src-qt5/core/libLumina/LuminaUtils.cpp877
-rw-r--r--src-qt5/core/libLumina/LuminaUtils.h128
-rw-r--r--src-qt5/core/libLumina/LuminaX11.cpp2172
-rw-r--r--src-qt5/core/libLumina/LuminaX11.h407
-rw-r--r--src-qt5/core/libLumina/LuminaXDG.cpp1157
-rw-r--r--src-qt5/core/libLumina/LuminaXDG.h168
-rw-r--r--src-qt5/core/libLumina/colors/Black.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Blue-Light.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Grey-Dark.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Lumina-Glass.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Lumina-Gold.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Lumina-Green.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Lumina-Purple.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Lumina-Red.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/PCBSD10-Default.qss.colors13
-rw-r--r--src-qt5/core/libLumina/colors/Solarized-Dark.qss.colors16
-rw-r--r--src-qt5/core/libLumina/colors/Solarized-Light.qss.colors16
-rw-r--r--src-qt5/core/libLumina/libLumina.pro65
-rw-r--r--src-qt5/core/libLumina/quickplugins/quick-sample.qml12
-rw-r--r--src-qt5/core/libLumina/themes/Lumina-default.qss.template488
-rw-r--r--src-qt5/core/libLumina/themes/None.qss.template118
35 files changed, 8979 insertions, 0 deletions
diff --git a/src-qt5/core/libLumina/LuminaOS-Debian.cpp b/src-qt5/core/libLumina/LuminaOS-Debian.cpp
new file mode 100644
index 00000000..75aad108
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-Debian.cpp
@@ -0,0 +1,300 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __linux__
+#include <QDebug>
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+
+QString LOS::OSName(){ return "Debian GNU/Linux"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return LOS::AppPrefix() + "/share/applications/synaptic.desktop"; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ /* Returns: QStringList[<type>::::<filesystem>::::<path>]
+ Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ df is much better for this than mount, because it skips
+ all non-physical devices (like bind-mounts from schroot)
+ so they are not listed in lumina-fm */
+ QStringList devs = LUtils::getCmdOutput("df --output=source,fstype,target");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ devs[i] = devs[i].simplified();
+ QString type = devs[i].section(" on ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("sd") || type.startsWith("nvme")){ type = "HDRIVE"; }
+ else if(type.startsWith("sr")){ type="DVD"; }
+ else if(type.contains("mapper")){ type="LVM"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type + "::::" + devs[i].section(" ",1,1) + "::::" + devs[i].section(" ",2,2);
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ return screenbrightness;
+
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ // float pf = percent/100.0; //convert to a decimel
+ //Run the command
+ QString cmd = "xbacklight -set %1";
+ // cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ cmd = cmd.arg( QString::number( percent ) );
+ int ret = LUtils::runCmd(cmd);
+ //Save the result for later
+ if(ret!=0){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+QString info = LUtils::getCmdOutput("amixer get Master").join("").simplified();;
+ int out = -1;
+ int start_position, end_position;
+ QString current_volume;
+ if(!info.isEmpty()){
+ start_position = info.indexOf("[");
+ start_position++;
+ end_position = info.indexOf("%");
+ current_volume = info.mid(start_position, end_position - start_position);
+ out = current_volume.toInt();
+ }
+ return out;
+
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ // QString info = "amixer -c 0 sset Master,0 " + QString::number(percent) + "%";
+ QString info = "amixer set Master " + QString::number(percent) + "%";
+ if(!info.isEmpty()){
+ //Run Command
+ LUtils::runCmd(info);
+ }
+
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ int old_volume = audioVolume();
+ int new_volume = old_volume + percentdiff;
+ if (new_volume < 0)
+ new_volume = 0;
+ if (new_volume > 100)
+ new_volume = 100;
+ qDebug() << "Setting new volume to: " << new_volume;
+ setAudioVolume(new_volume);
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return QFile::exists(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ QProcess::startDetached(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return QProcess::startDetached("dbus-send --system --print-reply=literal \
+ --type=method_call --dest=org.freedesktop.login1 \
+ /org/freedesktop/login1 org.freedesktop.login1.Manager.CanPowerOff");
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.PowerOff boolean:true");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.Reboot boolean:true");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return QProcess::startDetached("dbus-send --system --print-reply=literal \
+ --type=method_call --dest=org.freedesktop.login1 \
+ /org/freedesktop/login1 org.freedesktop.login1.Manager.CanSuspend");
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.Suspend boolean:true");
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool no_battery = my_status.contains("No support");
+ if (no_battery) return false;
+ return true;
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ int my_start = my_status.indexOf("%");
+ // get the number right before the % sign
+ int my_end = my_start;
+ my_start--;
+ while ( (my_status[my_start] != ' ') && (my_start > 0) )
+ my_start--;
+ my_start++;
+ int my_charge = my_status.mid(my_start, my_end - my_start).toInt();
+ if ( (my_charge < 0) || (my_charge > 100) ) return -1;
+ return my_charge;
+}
+
+//Battery Charging State
+// Many possible values are returned if the laptop is plugged in
+// these include "Unknown, Full and No support.
+// However, it seems just one status is returned when running
+// on battery and that is "Discharging". So if the value we get
+// is NOT Discharging then we assume the battery is charging.
+bool LOS::batteryIsCharging(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool discharging = my_status.contains("Discharging");
+ if (discharging) return false;
+ return true;
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet for Linux
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ QStringList info = LUtils::getCmdOutput("md5sum \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ // first: md5sum: = error ; second: there's always one empty entry generated by getCmdOutput
+ if( info[i].startsWith("md5sum:") || info[i].isEmpty()){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" ",0,0);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ QStringList mountInfo = LUtils::getCmdOutput("df -h \"" + dir + "\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //output: 200G of 400G available on /mount/point
+ QString capacity = mountInfo[1].section(" ",3,3, skipEmpty) + " of " + mountInfo[1].section(" ",1,1, skipEmpty) + " available on " + mountInfo[1].section(" ",5,5, skipEmpty);
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ QStringList temp = LUtils::getCmdOutput("acpi -t").filter("degrees");
+ for(int i=0; i<temp.length(); i++){
+ if(temp[i].startsWith("Thermal")){
+ temp[i] = temp[i].section(" ", 4, 6);
+ }else{
+ temp.removeAt(i); i--;
+ }
+ }
+ if(temp.isEmpty()) {
+ temp << "Not available";
+ }
+ return temp;
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ QStringList info = LUtils::getCmdOutput("top -bn1").filter("Cpu(s)");
+ if(info.isEmpty()){ return -1; }
+ QString idle = info.first().section(" ", 7, 7, QString::SectionSkipEmpty);
+ if(idle.isEmpty()){ return -1; }
+ else{
+ return (100 - idle.toDouble());
+ }
+}
+
+int LOS::MemoryUsagePercent(){
+ QStringList mem = LUtils::getCmdOutput("top -bn1").filter("Mem :");
+ if(mem.isEmpty()){ return -1; }
+ double fB = 0; //Free Bytes
+ double uB = 0; //Used Bytes
+ fB = mem.first().section(" ", 5, 5, QString::SectionSkipEmpty).toDouble();
+ uB = mem.first().section(" ", 7, 7, QString::SectionSkipEmpty).toDouble();
+ double per = (uB/(fB+uB)) * 100.0;
+ return qRound(per);
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ QStringList info = LUtils::getCmdOutput("iostat -dx -N");
+ if(info.length()<3){ return QStringList(); } //nothing from command
+ QStringList labs = info[1].split(" ",QString::SkipEmptyParts);
+ QStringList out;
+ QString fmt = "%1: %2 %3";
+ for(int i=2; i<info.length(); i++){ //skip the first two lines, just labels
+ info[i].replace("\t"," ");
+ if(info[i].startsWith("Device:")){ labs = info[i].split(" ", QString::SkipEmptyParts); }//the labels for each column
+ else{
+ QStringList data = info[i].split(" ",QString::SkipEmptyParts); //data[0] is always the device
+ if(data.length()>2 && labs.length()>2){
+ out << fmt.arg(data[0], data[3]+" "+labs[3], data[4]+" "+labs[4]);
+ }
+ }
+ }
+
+ return out;
+}
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-DragonFly.cpp b/src-qt5/core/libLumina/LuminaOS-DragonFly.cpp
new file mode 100644
index 00000000..b98a36ee
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-DragonFly.cpp
@@ -0,0 +1,355 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __DragonFly__
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/sensors.h>
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+static int audiovolume = -1;
+
+static bool get_sysctlbyname_int(const char *name, int *res) {
+ int r = 0;
+ size_t len = sizeof(r);
+ if (sysctlbyname(name, &r, &len, NULL, 0) == 0) {
+ *res = r;
+ return true;
+ }
+ return false;
+}
+
+#if 0
+static bool get_sysctlbyname_qstr(const char *name, QString &str) {
+ size_t len = 0;
+ sysctlbyname(name, NULL, &len, NULL, 0);
+ if (len > 0) {
+ void *buf = malloc(len);
+ if (buf) {
+ int res = sysctlbyname(name, buf, &len, NULL, 0);
+ if (res == 0) {
+ str = QString((char*) buf);
+ }
+ free(buf);
+ return (res == 0);
+ }
+ }
+ return false;
+}
+#endif
+
+// returns -1 on error.
+static int get_sysctlbyname_int(const char *name) {
+ int res = -1;
+ if (get_sysctlbyname_int(name, &res)) {
+ return res;
+ }
+ return -1;
+}
+
+static bool get_sysctlbyname_uint(const char *name, unsigned int *res) {
+ unsigned int r = 0;
+ size_t len = sizeof(r);
+ if (sysctlbyname(name, &r, &len, NULL, 0) == 0) {
+ *res = r;
+ return true;
+ }
+ return false;
+}
+
+QString LOS::OSName(){ return "DragonFly BSD"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/local/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ QStringList devs = LUtils::getCmdOutput("mount");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ QString type = devs[i].section(" on ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("da")){ type = "USB"; }
+ else if(type.startsWith("ada")){ type = "HDRIVE"; }
+ else if(type.startsWith("mmsd")){ type = "SDCARD"; }
+ else if(type.startsWith("cd")||type.startsWith("acd")){ type="DVD"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type+"::::"+devs[i].section("(",1,1).section(",",0,0)+"::::"+devs[i].section(" on ",1,50).section("(",0,0).simplified();
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ //If it gets to this point, then we have a valid (but new) installation
+ if(screenbrightness<0){ screenbrightness = 100; } //default value for systems
+
+ return screenbrightness;
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ if(percent == -1){ return; } //This is usually an invalid value passed directly to the setter
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ //Run the command(s)
+ bool success = false;
+ float pf = percent/100.0; //convert to a decimel
+ //Run the command
+ QString cmd = "xbrightness %1";
+ cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ success = (0 == LUtils::runCmd(cmd) );
+ //Save the result for later
+ if(!success){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ int out = audiovolume;
+ if(out < 0){
+ //First time session check: Load the last setting for this user
+ QString info = LUtils::readFile(QDir::homePath()+"/.lumina/.currentvolume").join("");
+ if(!info.isEmpty()){
+ out = info.simplified().toInt();
+ audiovolume = out; //unset this internal flag
+ return out;
+ }
+ }
+
+ //probe the system for the current volume (other utils could be changing it)
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt();
+ int R = info.section(":",2,2).toInt();
+ if(L>R){ out = L; }
+ else{ out = R; }
+ if(out != audiovolume){
+ //Volume changed by other utility: adjust the saved value as well
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentvolume", QStringList() << QString::number(out), true);
+ }
+ audiovolume = out;
+ }
+
+ return out;
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt();
+ int R = info.section(":",2,2).toInt();
+ int diff = L-R;
+ if((percent == L) && (L==R)){ return; } //already set to that volume
+ if(diff<0){ R=percent; L=percent+diff; } //R Greater
+ else{ L=percent; R=percent-diff; } //L Greater or equal
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ audiovolume = percent; //save for checking later
+ LUtils::runCmd("mixer vol "+QString::number(L)+":"+QString::number(R));
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentvolume", QStringList() << QString::number(percent), true);
+ }
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt() + percentdiff;
+ int R = info.section(":",2,2).toInt() + percentdiff;
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ LUtils::runCmd("mixer vol "+QString::number(L)+":"+QString::number(R));
+ }
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return false; //not implemented yet for DragonFly
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ //Not implemented yet for DragonFly
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return true; //not implemented yet
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -p now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ return (get_sysctlbyname_int("hw.acpi.battery.units") >= 1);
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ int charge = get_sysctlbyname_int("hw.acpi.battery.life");
+ if(charge > 100){ charge = -1; } //invalid charge
+ return charge;
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return (get_sysctlbyname_int("hw.acpi.battery.state") == 0);
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ int time = get_sysctlbyname_int("hw.acpi.battery.time");
+ if (time > 0) {
+ // time is in minutes
+ time *= 60;
+ }
+ return time;
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ if( !info[i].contains(" = ") ){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" = ",1,1);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ QStringList mountInfo = LUtils::getCmdOutput("df \"" + dir+"\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //we take the 5th word on the 2 line
+ QString capacity = mountInfo[1].section(" ",4,4, skipEmpty);
+ return capacity;
+}
+
+static float sensor_value_to_degC(int64_t value) {
+ return (value - 273150000) / 1000000.0;
+}
+
+//Returns: List containing the temperature of any CPU's ("50C" for example)
+QStringList LOS::CPUTemperatures(){
+ QStringList temps;
+
+ int mib[5];
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+
+ for (int dev=0; dev < MAXSENSORDEVICES; ++dev) {
+ struct sensordev sensordev;
+ size_t sdlen = sizeof(sensordev);
+
+ mib[2] = dev;
+ if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
+ continue;
+ }
+ mib[3] = SENSOR_TEMP;
+ for (int numt=0; numt < sensordev.maxnumt[SENSOR_TEMP]; ++numt) {
+ mib[4] = numt;
+ struct sensor sensor;
+ size_t slen = sizeof(sensor);
+ if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
+ continue;
+ }
+
+ // XXX: Filter out non-cpu temperatures
+
+ int degC = (int)sensor_value_to_degC(sensor.value);
+ temps << QString::number(degC) + "C" + "(" + QString(sensordev.xname) + ")";
+ }
+ }
+
+ return temps;
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ //SYSCTL: vm.stats.vm.v_<something>_count
+ unsigned int v_page_count = 0;
+ unsigned int v_wire_count = 0;
+ unsigned int v_active_count = 0;
+
+ if (!get_sysctlbyname_uint("vm.stats.vm.v_page_count", &v_page_count)) return -1;
+ if (!get_sysctlbyname_uint("vm.stats.vm.v_wire_count", &v_wire_count)) return -1;
+ if (!get_sysctlbyname_uint("vm.stats.vm.v_active_count", &v_active_count)) return -1;
+
+ //List output: [total, wired, active]
+ double perc = 100.0 * ((long)v_wire_count+(long)v_active_count)/((double)v_page_count);
+ return qRound(perc);
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-FreeBSD.cpp b/src-qt5/core/libLumina/LuminaOS-FreeBSD.cpp
new file mode 100644
index 00000000..72abf0eb
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-FreeBSD.cpp
@@ -0,0 +1,362 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __FreeBSD__
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <QDebug>
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+static int audiovolume = -1;
+
+QString LOS::OSName(){ return "FreeBSD"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/local/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return "/usr/local/share/applications/pccontrol.desktop"; } //system control panel
+QString LOS::AppStoreShortcut(){ return "/usr/local/share/applications/softmanager.desktop"; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ QStringList devs = LUtils::getCmdOutput("mount");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ devs[i].replace("\t"," ");
+ QString type = devs[i].section(" on ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("da")){ type = "USB"; }
+ else if(type.startsWith("ada")){ type = "HDRIVE"; }
+ else if(type.startsWith("mmsd")){ type = "SDCARD"; }
+ else if(type.startsWith("cd")||type.startsWith("acd")){ type="DVD"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type+"::::"+devs[i].section("(",1,1).section(",",0,0)+"::::"+devs[i].section(" on ",1,50).section("(",0,0).simplified();
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ //Make sure we are not running in VirtualBox (does not work in a VM)
+ QStringList info = LUtils::getCmdOutput("pciconf -lv");
+ if( !info.filter("VirtualBox", Qt::CaseInsensitive).isEmpty() ){ return -1; }
+ else if( !LUtils::isValidBinary("xbrightness") ){ return -1; } //incomplete install
+ //Now perform the standard brightness checks
+ if(screenbrightness==-1){ //memory value
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){ //saved file value
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ //If it gets to this point, then we have a valid (but new) installation
+ if(screenbrightness<0){ screenbrightness = 100; } //default value for systems
+
+ return screenbrightness;
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ if(percent == -1){ return; } //This is usually an invalid value passed directly to the setter
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ //Run the command(s)
+ bool success = false;
+ // - try hardware setting first (PC-BSD || or intel_backlight)
+ if( LUtils::isValidBinary("pc-sysconfig") ){
+ //Use PC-BSD tool (direct sysctl control)
+ QString ret = LUtils::getCmdOutput("pc-sysconfig", QStringList() <<"setscreenbrightness "+QString::number(percent)).join("");
+ success = ret.toLower().contains("success");
+ qDebug() << "Set hardware brightness:" << percent << success;
+ }
+ if( !success && LUtils::isValidBinary("intel_backlight")){
+ //Use the intel_backlight utility (only for Intel mobo/hardware?)
+ if(0== LUtils::runCmd("intel_backlight", QStringList() <<QString::number(percent)) ){
+ //This utility does not report success/failure - run it again to get the current value and ensure it was set properly
+ success = (percent == LUtils::getCmdOutput("intel_backlight").join("").section("%",0,0).section(":",1,1).simplified().toInt() );
+ }
+ }
+ // - if hardware brightness does not work, use software brightness
+ if(!success){
+ QString cmd = "xbrightness %1";
+ float pf = percent/100.0; //convert to a decimel
+ cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ success = (0 == LUtils::runCmd(cmd) );
+ }
+ //Save the result for later
+ if(!success){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ int out = audiovolume;
+ if(out < 0){
+ //First time session check: Load the last setting for this user
+ QString info = LUtils::readFile(QDir::homePath()+"/.lumina/.currentvolume").join("");
+ if(!info.isEmpty()){
+ out = info.simplified().toInt();
+ audiovolume = out; //unset this internal flag
+ return out;
+ }
+ }
+
+ //probe the system for the current volume (other utils could be changing it)
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt();
+ int R = info.section(":",2,2).toInt();
+ if(L>R){ out = L; }
+ else{ out = R; }
+ if(out != audiovolume){
+ //Volume changed by other utility: adjust the saved value as well
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentvolume", QStringList() << QString::number(out), true);
+ }
+ audiovolume = out;
+ }
+
+ return out;
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt();
+ int R = info.section(":",2,2).toInt();
+ int diff = L-R;
+ if((percent == L) && (L==R)){ return; } //already set to that volume
+ if(diff<0){ R=percent; L=percent+diff; } //R Greater
+ else{ L=percent; R=percent-diff; } //L Greater or equal
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ audiovolume = percent; //save for checking later
+ LUtils::runCmd("mixer vol "+QString::number(L)+":"+QString::number(R));
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentvolume", QStringList() << QString::number(percent), true);
+ }
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ QString info = LUtils::getCmdOutput("mixer -S vol").join(":").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(":",1,1).toInt() + percentdiff;
+ int R = info.section(":",2,2).toInt() + percentdiff;
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ LUtils::runCmd("mixer vol "+QString::number(L)+":"+QString::number(R));
+ }
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return QFile::exists("/usr/local/bin/pc-mixer");
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ QProcess::startDetached("pc-mixer -notray");
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ //User needs to be a part of the operator group to be able to run the shutdown command
+ QStringList groups = LUtils::getCmdOutput("id -Gn").join(" ").split(" ");
+ return groups.contains("operator");
+}
+
+bool LOS::systemPerformingUpdates(){
+ return (QProcess::execute("pgrep -F /tmp/.updateInProgress")==0); //this is 0 if updating right now
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -p now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ //This will only function on PC-BSD
+ //(permissions issues on standard FreeBSD unless setup a special way)
+ bool ok = QFile::exists("/usr/local/bin/pc-sysconfig");
+ if(ok){
+ ok = LUtils::getCmdOutput("pc-sysconfig systemcansuspend").join("").toLower().contains("true");
+ }
+ return ok;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+ QProcess::startDetached("pc-sysconfig suspendsystem");
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ int val = LUtils::getCmdOutput("apm -l").join("").toInt();
+ return (val >= 0 && val <= 100);
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ int charge = LUtils::getCmdOutput("apm -l").join("").toInt();
+ if(charge > 100){ charge = -1; } //invalid charge
+ return charge;
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return (LUtils::getCmdOutput("apm -a").join("").simplified() == "1");
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return LUtils::getCmdOutput("apm -t").join("").toInt();
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ if( !info[i].contains(" = ") ){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" = ",1,1);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ QStringList mountInfo = LUtils::getCmdOutput("df \"" + dir+"\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //we take the 5th word on the 2 line
+ QString capacity = mountInfo[1].section(" ",4,4, skipEmpty);
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ static QStringList vars = QStringList();
+ QStringList temps;
+ if(vars.isEmpty()){ temps = LUtils::getCmdOutput("sysctl -ai").filter(".temperature:"); }
+ else{ temps = LUtils::getCmdOutput("sysctl "+vars.join(" ")); vars.clear(); }
+
+ temps.sort();
+ for(int i=0; i<temps.length(); i++){
+ if(temps[i].contains(".acpi.") || temps[i].contains(".cpu")){
+ vars << temps[i].section(":",0,0); //save this variable for later checks
+ temps[i] = temps[i].section(":",1,5).simplified(); //only pull out the value, not the variable
+ }else{
+ //non CPU temperature - skip it
+ temps.removeAt(i); i--;
+ }
+ }
+ /*}else{
+ //Already have the known variables - use the library call directly (much faster)
+ for(int i=0; i<vars.length(); i++){
+ float result[1000];
+ size_t len = sizeof(result);
+ if(0 != sysctlbyname(vars[i].toLocal8Bit(), result, &len, NULL, 0)){ continue; } //error returned
+ //result[len] = '\0'; //make sure to null-terminate the output
+ QString res;
+ for(int r=0; r<((int) len); r++){ res.append(QString::number(result[r])); }
+ temps << res;
+ qDebug() << "Temp Result:" << vars[i] << res << result << len;
+ }
+ }*/
+ return temps;
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ //Calculate the percentage based on the kernel information directly - no extra utilities
+ QStringList result = LUtils::getCmdOutput("sysctl -n kern.cp_times").join("").split(" ");
+ static QStringList last = QStringList();
+ if(last.isEmpty()){ last = result; return 0; } //need two ticks before it works properly
+
+ double tot = 0;
+ int cpnum = 0;
+ for(int i=4; i<result.length(); i+=5){
+ //The values come in blocks of 5 per CPU: [user,nice,system,interrupt,idle]
+ cpnum++; //the number of CPU's accounted for (to average out at the end)
+ //qDebug() <<"CPU:" << cpnum;
+ long sum = 0;
+ //Adjust/all the data to correspond to diffs from the previous check
+ for(int j=0; j<5; j++){
+ QString tmp = result[i-j];
+ result[i-j] = QString::number(result[i-j].toLong()-last[i-j].toLong()); //need the difference between last run and this one
+ sum += result[i-j].toLong();
+ last[i-j] = tmp; //make sure to keep the original value around for the next run
+ }
+ //Calculate the percentage used for this CPU (100% - IDLE%)
+ tot += 100.0L - ( (100.0L*result[i].toLong())/sum ); //remember IDLE is the last of the five values per CPU
+ }
+ return qRound(tot/cpnum);
+
+}
+
+int LOS::MemoryUsagePercent(){
+ //SYSCTL: vm.stats.vm.v_<something>_count
+ QStringList info = LUtils::getCmdOutput("sysctl -n vm.stats.vm.v_page_count vm.stats.vm.v_wire_count vm.stats.vm.v_active_count");
+ if(info.length()<3){ return -1; } //error in fetching information
+ //List output: [total, wired, active]
+ double perc = 100.0* (info[1].toLong()+info[2].toLong())/(info[0].toDouble());
+ return qRound(perc);
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ QStringList info = LUtils::getCmdOutput("iostat -dx -c 2 -w 0.1 -t IDE -t SCSI -t da");
+ //Note: This returns the statistics *twice*: the first set is average for entire system
+ // - the second set is the actual average stats over that 0.1 second
+ if(info.length()<6){ return QStringList(); } //nothing from command
+ QStringList labs = info[1].split(" ",QString::SkipEmptyParts);
+ QStringList out;
+ QString fmt = "%1: %2 %3";
+ for(int i=(info.length()/2)+2; i<info.length(); i++){ //skip the first data entry , just labels
+ info[i].replace("\t"," ");
+ if(i==1){ labs = info[i].split(" ", QString::SkipEmptyParts); }//the labels for each column
+ else{
+ QStringList data = info[i].split(" ",QString::SkipEmptyParts); //data[0] is always the device
+ //qDebug() << "Data Line:" << data;
+ if(data.length()>2 && labs.length()>2){
+ out << fmt.arg(data[0], data[1]+" "+labs[1], data[2]+" "+labs[2]);
+ }
+ }
+ }
+
+ return out;
+}
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-Gentoo.cpp b/src-qt5/core/libLumina/LuminaOS-Gentoo.cpp
new file mode 100644
index 00000000..e3d5fe56
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-Gentoo.cpp
@@ -0,0 +1,300 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2016, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __linux__
+#include <QDebug>
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+
+QString LOS::OSName(){ return "Gentoo Linux"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return LOS::AppPrefix() + "/share/applications/porthole.desktop"; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ /* Returns: QStringList[<type>::::<filesystem>::::<path>]
+ Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ df is much better for this than mount, because it skips
+ all non-physical devices (like bind-mounts from schroot)
+ so they are not listed in lumina-fm */
+ QStringList devs = LUtils::getCmdOutput("df --output=source,fstype,target");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ devs[i] = devs[i].simplified();
+ QString type = devs[i].section(" on ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("sd") || type.startsWith("nvme")){ type = "HDRIVE"; }
+ else if(type.startsWith("sr")){ type="DVD"; }
+ else if(type.contains("mapper")){ type="LVM"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type + "::::" + devs[i].section(" ",1,1) + "::::" + devs[i].section(" ",2,2);
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ return screenbrightness;
+
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ // float pf = percent/100.0; //convert to a decimel
+ //Run the command
+ QString cmd = "xbacklight -set %1";
+ // cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ cmd = cmd.arg( QString::number( percent ) );
+ int ret = LUtils::runCmd(cmd);
+ //Save the result for later
+ if(ret!=0){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+QString info = LUtils::getCmdOutput("amixer get Master").join("").simplified();;
+ int out = -1;
+ int start_position, end_position;
+ QString current_volume;
+ if(!info.isEmpty()){
+ start_position = info.indexOf("[");
+ start_position++;
+ end_position = info.indexOf("%");
+ current_volume = info.mid(start_position, end_position - start_position);
+ out = current_volume.toInt();
+ }
+ return out;
+
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ // QString info = "amixer -c 0 sset Master,0 " + QString::number(percent) + "%";
+ QString info = "amixer set Master " + QString::number(percent) + "%";
+ if(!info.isEmpty()){
+ //Run Command
+ LUtils::runCmd(info);
+ }
+
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ int old_volume = audioVolume();
+ int new_volume = old_volume + percentdiff;
+ if (new_volume < 0)
+ new_volume = 0;
+ if (new_volume > 100)
+ new_volume = 100;
+ qDebug() << "Setting new volume to: " << new_volume;
+ setAudioVolume(new_volume);
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return QFile::exists(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ QProcess::startDetached(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return QProcess::startDetached("dbus-send --system --print-reply=literal \
+ --type=method_call --dest=org.freedesktop.login1 \
+ /org/freedesktop/login1 org.freedesktop.login1.Manager.CanPowerOff");
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.PowerOff boolean:true");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.Reboot boolean:true");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return QProcess::startDetached("dbus-send --system --print-reply=literal \
+ --type=method_call --dest=org.freedesktop.login1 \
+ /org/freedesktop/login1 org.freedesktop.login1.Manager.CanSuspend");
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+ QProcess::startDetached("dbus-send --system --print-reply \
+ --dest=org.freedesktop.login1 /org/freedesktop/login1 \
+ org.freedesktop.login1.Manager.Suspend boolean:true");
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool no_battery = my_status.contains("No support");
+ if (no_battery) return false;
+ return true;
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ int my_start = my_status.indexOf("%");
+ // get the number right before the % sign
+ int my_end = my_start;
+ my_start--;
+ while ( (my_status[my_start] != ' ') && (my_start > 0) )
+ my_start--;
+ my_start++;
+ int my_charge = my_status.mid(my_start, my_end - my_start).toInt();
+ if ( (my_charge < 0) || (my_charge > 100) ) return -1;
+ return my_charge;
+}
+
+//Battery Charging State
+// Many possible values are returned if the laptop is plugged in
+// these include "Unknown, Full and No support.
+// However, it seems just one status is returned when running
+// on battery and that is "Discharging". So if the value we get
+// is NOT Discharging then we assume the battery is charging.
+bool LOS::batteryIsCharging(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool discharging = my_status.contains("Discharging");
+ if (discharging) return false;
+ return true;
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet for Linux
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ QStringList info = LUtils::getCmdOutput("md5sum \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ // first: md5sum: = error ; second: there's always one empty entry generated by getCmdOutput
+ if( info[i].startsWith("md5sum:") || info[i].isEmpty()){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" ",0,0);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ QStringList mountInfo = LUtils::getCmdOutput("df -h \"" + dir + "\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //output: 200G of 400G available on /mount/point
+ QString capacity = mountInfo[1].section(" ",3,3, skipEmpty) + " of " + mountInfo[1].section(" ",1,1, skipEmpty) + " available on " + mountInfo[1].section(" ",5,5, skipEmpty);
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ QStringList temp = LUtils::getCmdOutput("acpi -t").filter("degrees");
+ for(int i=0; i<temp.length(); i++){
+ if(temp[i].startsWith("Thermal")){
+ temp[i] = temp[i].section(" ", 4, 6);
+ }else{
+ temp.removeAt(i); i--;
+ }
+ }
+ if(temp.isEmpty()) {
+ temp << "Not available";
+ }
+ return temp;
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ QStringList info = LUtils::getCmdOutput("top -bn1").filter("Cpu(s)");
+ if(info.isEmpty()){ return -1; }
+ QString idle = info.first().section(" ", 7, 7, QString::SectionSkipEmpty);
+ if(idle.isEmpty()){ return -1; }
+ else{
+ return (100 - idle.toDouble());
+ }
+}
+
+int LOS::MemoryUsagePercent(){
+ QStringList mem = LUtils::getCmdOutput("top -bn1").filter("Mem :");
+ if(mem.isEmpty()){ return -1; }
+ double fB = 0; //Free Bytes
+ double uB = 0; //Used Bytes
+ fB = mem.first().section(" ", 5, 5, QString::SectionSkipEmpty).toDouble();
+ uB = mem.first().section(" ", 7, 7, QString::SectionSkipEmpty).toDouble();
+ double per = (uB/(fB+uB)) * 100.0;
+ return qRound(per);
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ QStringList info = LUtils::getCmdOutput("iostat -dx -N");
+ if(info.length()<3){ return QStringList(); } //nothing from command
+ QStringList labs = info[1].split(" ",QString::SkipEmptyParts);
+ QStringList out;
+ QString fmt = "%1: %2 %3";
+ for(int i=2; i<info.length(); i++){ //skip the first two lines, just labels
+ info[i].replace("\t"," ");
+ if(info[i].startsWith("Device:")){ labs = info[i].split(" ", QString::SkipEmptyParts); }//the labels for each column
+ else{
+ QStringList data = info[i].split(" ",QString::SkipEmptyParts); //data[0] is always the device
+ if(data.length()>2 && labs.length()>2){
+ out << fmt.arg(data[0], data[3]+" "+labs[3], data[4]+" "+labs[4]);
+ }
+ }
+ }
+
+ return out;
+}
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-Linux.cpp b/src-qt5/core/libLumina/LuminaOS-Linux.cpp
new file mode 100644
index 00000000..5939c9d1
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-Linux.cpp
@@ -0,0 +1,245 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __linux__
+#include <QDebug>
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+
+QString LOS::OSName(){ return "Linux"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ QStringList devs = LUtils::getCmdOutput("mount");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ devs[i] = devs[i].simplified();
+ QString type = devs[i].section(" ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("sd") || type.startsWith("nvme")){ type = "HDRIVE"; }
+ else if(type.startsWith("sr")){ type="DVD"; }
+ else if(type.contains("mapper")){ type="LVM"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type+"::::"+devs[i].section(" ",4,4)+"::::"+devs[i].section(" ",2,2);
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ return screenbrightness;
+
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ // float pf = percent/100.0; //convert to a decimel
+ //Run the command
+ QString cmd = "xbacklight -set %1";
+ // cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ cmd = cmd.arg( QString::number( percent ) );
+ int ret = LUtils::runCmd(cmd);
+ //Save the result for later
+ if(ret!=0){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+QString info = LUtils::getCmdOutput("amixer get Master").join("").simplified();;
+ int out = -1;
+ int start_position, end_position;
+ QString current_volume;
+ if(!info.isEmpty()){
+ start_position = info.indexOf("[");
+ start_position++;
+ end_position = info.indexOf("%");
+ current_volume = info.mid(start_position, end_position - start_position);
+ out = current_volume.toInt();
+ }
+ return out;
+
+
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ QString info = "amixer set Master " + QString::number(percent) + "%";
+ //Run Command
+ LUtils::runCmd(info);
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ int old_volume = audioVolume();
+ int new_volume = old_volume + percentdiff;
+ if (new_volume < 0)
+ new_volume = 0;
+ if (new_volume > 100)
+ new_volume = 100;
+ qDebug() << "Setting new volume to: " << new_volume;
+ setAudioVolume(new_volume);
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return QFile::exists(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ QProcess::startDetached(LOS::AppPrefix() + "bin/pavucontrol");
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return true; //not implemented yet
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -P -h now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool no_battery = my_status.contains("No support");
+ if (no_battery) return false;
+ return true;
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ int my_start = my_status.indexOf("%");
+ // get the number right before the % sign
+ int my_end = my_start;
+ my_start--;
+ while ( (my_status[my_start] != ' ') && (my_start > 0) )
+ my_start--;
+ my_start++;
+ int my_charge = my_status.mid(my_start, my_end - my_start).toInt();
+ if ( (my_charge < 0) || (my_charge > 100) ) return -1;
+ return my_charge;
+}
+
+//Battery Charging State
+// Many possible values are returned if the laptop is plugged in
+// these include "Unknown, Full and No support.
+// However, it seems just one status is returned when running
+// on battery and that is "Discharging". So if the value we get
+// is NOT Discharging then we assume the battery is charging.
+bool LOS::batteryIsCharging(){
+ QString my_status = LUtils::getCmdOutput("acpi -b").join("");
+ bool discharging = my_status.contains("Discharging");
+ if (discharging) return false;
+ return true;
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet for Linux
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ QStringList info = LUtils::getCmdOutput("md5sum \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ // first: md5sum: = error ; second: there's always one empty entry generated by getCmdOutput
+ if( info[i].startsWith("md5sum:") || info[i].isEmpty()){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" ",0,0);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ QStringList mountInfo = LUtils::getCmdOutput("df \"" + dir+"\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //we take the 5th word on the 2 line
+ QString capacity = mountInfo[1].section(" ",4,4, skipEmpty) + " used";
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ return QStringList(); //not implemented yet
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ return -1; //not implemented yet
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-NetBSD.cpp b/src-qt5/core/libLumina/LuminaOS-NetBSD.cpp
new file mode 100644
index 00000000..866ccc5c
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-NetBSD.cpp
@@ -0,0 +1,169 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __NetBSD__
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+QString LOS::OSName(){ return "NetBSD"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/local/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+
+ //Not implemented yet
+ return QStringList();
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ return -1; //not implemented yet
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //not implemented yet
+}
+
+//Read the current volume
+int LOS::audioVolume(){
+ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ return -1; //Not implemented yet
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ //not implemented yet
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ //not implemented yet
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return false; //not implemented yet
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ //not implemented yet
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ //User needs to be a part of the operator group to be able to run the shutdown command
+ QStringList groups = LUtils::getCmdOutput("id -Gn").join(" ").split(" ");
+ return groups.contains("operator");
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -p now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ return false; //not implemented yet
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ return -1; //not implemented yet
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return false; //not implemented yet
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ //on NetBSD md5(1) has the following layout
+ //$ md5 DESCR Makefile
+ //MD5 (DESCR) = 6aaa128cf0466792be6f6d15e4589dfb
+ //MD5 (Makefile) = 4787f3145bba2a3cc393cdd812c5d18b
+
+ QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ if( !info[i].contains(" = ") ){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" = ",1,1);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ // on NetBSD, df has the following layout:
+ // $ df /home
+ // Filesystem 512-blocks Used Avail %Cap Mounted on
+ // /dev/cgd0a 39711132 37140376 585200 98% /home
+
+ QStringList mountInfo = LUtils::getCmdOutput("df \"" + dir+"\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //we take the 5th word on the 2 line
+ QString capacity = mountInfo[1].section(" ",4,4, skipEmpty);
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ return QStringList(); //not implemented yet
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ return -1; //not implemented yet
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-OpenBSD.cpp b/src-qt5/core/libLumina/LuminaOS-OpenBSD.cpp
new file mode 100644
index 00000000..c0fdafd4
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-OpenBSD.cpp
@@ -0,0 +1,257 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Copyright (c) 2014, Antoine Jacoutot <ajacoutot@openbsd.org>
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __OpenBSD__
+#include "LuminaOS.h"
+#include <unistd.h>
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+
+QString LOS::OSName(){ return "OpenBSD"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/local/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ QStringList devs = LUtils::getCmdOutput("mount");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ QString type = devs[i].section(" ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("sd")||type.startsWith("wd")){ type = "HDRIVE"; }
+ else if(type.startsWith("cd")){ type="DVD"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ QString fs = devs[i].section(" ", 4, 4);
+ QString path = devs[i].section(" ",2, 2);
+ if (!fs.isEmpty() ) { //we not found a filesystem, most probably this is an invalid row
+ devs[i] = type+"::::"+fs+"::::"+path;
+ }
+ else {
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ //Make sure we are not running in a VM (does not work)
+ QStringList info = LUtils::getCmdOutput("sysctl -n hw.product");
+ if( !info.filter(QRegExp("VirtualBox|KVM")).isEmpty() ){ return -1; }
+ //Now perform the standard brightness checks
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ return screenbrightness;
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ //Run the command
+ QString cmd = "xbacklight -time 0 -steps 1 -set %1";
+ cmd = cmd.arg( QString::number(percent) );
+ int ret = LUtils::runCmd(cmd);
+ //Save the result for later
+ if(ret!=0){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ QString info = LUtils::getCmdOutput("mixerctl -n outputs.master").join(",").simplified(); //ignores any other lines
+ int out = -1;
+ if(!info.isEmpty()){
+ int L = info.section(",",0,0).toInt();
+ int R = info.section(",",1,1).toInt();
+ L = (L*100)/255; //percent
+ R = (R*100)/255; //percent
+ if(L>R){ out = L; }
+ else{ out = R; }
+ }
+ return out;
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ if(percent<0){percent=0;}
+ else if(percent>100){percent=100;}
+ QString info = LUtils::getCmdOutput("mixerctl -n outputs.master").join(",").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(",",0,0).toInt();
+ int R = info.section(",",1,1).toInt();
+ L = (L*100)/255; //percent
+ R = (R*100)/255; //percent
+ int diff = L-R;
+ if(diff<0){ R=percent; L=percent+diff; } //R Greater
+ else{ L=percent; R=percent-diff; } //L Greater or equal
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ L = (L*255)/100; //0-255
+ R = (R*255)/100; //0-255
+ LUtils::runCmd("mixerctl -q outputs.master="+QString::number(L)+","+QString::number(R));
+ }
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ QString info = LUtils::getCmdOutput("mixerctl -n outputs.master").join(",").simplified(); //ignores any other lines
+ if(!info.isEmpty()){
+ int L = info.section(",",0,0).toInt();
+ int R = info.section(",",1,1).toInt();
+ L = (L*100)/255; //percent
+ R = (R*100)/255; //percent
+ L = L + percentdiff;
+ R = R + percentdiff;
+ //Check bounds
+ if(L<0){L=0;}else if(L>100){L=100;}
+ if(R<0){R=0;}else if(R>100){R=100;}
+ //Run Command
+ L = (L*255)/100; //0-255
+ R = (R*255)/100; //0-255
+ LUtils::runCmd("mixerctl -q outputs.master="+QString::number(L)+","+QString::number(R));
+ }
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return false; //not implemented yet for OpenBSD
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ //Not implemented yet for OpenBSD
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ //User needs to be a part of the operator group to be able to run the shutdown command
+ QStringList groups = LUtils::getCmdOutput("id -Gn").join(" ").split(" ");
+ return groups.contains("operator");
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -p now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ int val = LUtils::getCmdOutput("apm -b").join("").toInt();
+ return (val < 4);
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ int charge = LUtils::getCmdOutput("apm -l").join("").toInt();
+ if(charge > 100){ charge = -1; } //invalid charge
+ return charge;
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return (LUtils::getCmdOutput("apm -a").join("").simplified() == "1");
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ int min = LUtils::getCmdOutput("apm -m").join("").toInt();
+ return (min * 60);
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ //on OpenBSD md5 has the following layout
+ //>md5 LuminaThemes.o LuminaUtils.o
+ //MD5 (LuminaThemes.o) = 50006505d9d7e54e5154eeb095555055
+ //MD5 (LuminaUtils.o) = d490878ee8866e55e5af571b98b4d448
+
+ QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\"");
+ for(int i=0; i<info.length(); i++){
+ if( !info[i].contains(" = ") ){ info.removeAt(i); i--; }
+ else{
+ //Strip out the extra information
+ info[i] = info[i].section(" = ",1,1);
+ }
+ }
+ return info;
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) {
+ // on OpenBSD, df has the following layout:
+ //>df /home/wi
+ //>Filesystem 512-blocks Used Avail Capacity Mounted on
+ //>/dev/sd2l 14334588 739900 12877960 5% /home
+
+ QStringList mountInfo = LUtils::getCmdOutput("df \"" + dir+"\"");
+ QString::SectionFlag skipEmpty = QString::SectionSkipEmpty;
+ //we take the 5th word on the 2 line
+ QString capacity = mountInfo[1].section(" ",4,4, skipEmpty);
+ return capacity;
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ return QStringList(); //not implemented yet
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ return -1; //not implemented yet
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-kFreeBSD.cpp b/src-qt5/core/libLumina/LuminaOS-kFreeBSD.cpp
new file mode 100644
index 00000000..4fe62686
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-kFreeBSD.cpp
@@ -0,0 +1,198 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __FreeBSD_kernel__
+#ifndef __FreeBSD__
+// The above two checks should make sure that we are on a
+// operating system using the FreeBSD kernel without actually being
+// on FreeBSD. That probably means Debian's kFreeBSD port.
+#include <QDebug>
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+//can't read xbrightness settings - assume invalid until set
+static int screenbrightness = -1;
+
+QString LOS::OSName(){ return "Debian GNU/kFreeBSD"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+ QStringList devs = LUtils::getCmdOutput("mount");
+ //Now check the output
+ for(int i=0; i<devs.length(); i++){
+ if(devs[i].startsWith("/dev/")){
+ QString type = devs[i].section(" on ",0,0);
+ type.remove("/dev/");
+ //Determine the type of hardware device based on the dev node
+ if(type.startsWith("da")){ type = "USB"; }
+ else if(type.startsWith("ada")){ type = "HDRIVE"; }
+ else if(type.startsWith("mmsd")){ type = "SDCARD"; }
+ else if(type.startsWith("cd")||type.startsWith("acd")){ type="DVD"; }
+ else{ type = "UNKNOWN"; }
+ //Now put the device in the proper output format
+ devs[i] = type+"::::"+devs[i].section("(",1,1).section(",",0,0)+"::::"+devs[i].section(" on ",1,50).section("(",0,0).simplified();
+ }else{
+ //invalid device - remove it from the list
+ devs.removeAt(i);
+ i--;
+ }
+ }
+ return devs;
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ if(screenbrightness==-1){
+ if(QFile::exists(QDir::homePath()+"/.lumina/.currentxbrightness")){
+ int val = LUtils::readFile(QDir::homePath()+"/.lumina/.currentxbrightness").join("").simplified().toInt();
+ screenbrightness = val;
+ }
+ }
+ return screenbrightness;
+
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //ensure bounds
+ if(percent<0){percent=0;}
+ else if(percent>100){ percent=100; }
+ // float pf = percent/100.0; //convert to a decimel
+ //Run the command
+ QString cmd = "xbacklight -set %1";
+ // cmd = cmd.arg( QString::number( int(65535*pf) ) );
+ cmd = cmd.arg( QString::number( percent ) );
+ int ret = LUtils::runCmd(cmd);
+ //Save the result for later
+ if(ret!=0){ screenbrightness = -1; }
+ else{ screenbrightness = percent; }
+ LUtils::writeFile(QDir::homePath()+"/.lumina/.currentxbrightness", QStringList() << QString::number(screenbrightness), true);
+
+}
+
+//Read the current volume
+int LOS::audioVolume(){ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ return -1; // not available on kFreeBSD yet
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ return;
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ int old_volume = audioVolume();
+ int new_volume = old_volume + percentdiff;
+ if (new_volume < 0)
+ new_volume = 0;
+ if (new_volume > 100)
+ new_volume = 100;
+ qDebug() << "Setting new volume to: " << new_volume;
+ setAudioVolume(new_volume);
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return QFile::exists("/usr/bin/pavucontrol");
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ QProcess::startDetached("/usr/bin/pavucontrol");
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return true; //not implemented yet
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -h now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ return false;
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ return -1;
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return false;
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet for Linux
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ return QStringList();
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ return QString();
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ return QStringList(); //not implemented yet
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ return -1; //not implemented yet
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+#endif
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS-template.cpp b/src-qt5/core/libLumina/LuminaOS-template.cpp
new file mode 100644
index 00000000..5969bf3a
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS-template.cpp
@@ -0,0 +1,145 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifdef __OSNAME__
+#include "LuminaOS.h"
+#include <unistd.h>
+#include <stdio.h> // Needed for BUFSIZ
+
+QString LOS::OSName(){ return "Sample"; }
+
+//OS-specific prefix(s)
+// NOTE: PREFIX, L_ETCDIR, L_SHAREDIR are defined in the OS-detect.pri project file and passed in
+QString LOS::LuminaShare(){ return (L_SHAREDIR+"/Lumina-DE/"); } //Install dir for Lumina share files
+QString LOS::AppPrefix(){ return "/usr/local/"; } //Prefix for applications
+QString LOS::SysPrefix(){ return "/usr/"; } //Prefix for system
+
+//OS-specific application shortcuts (*.desktop files)
+QString LOS::ControlPanelShortcut(){ return ""; } //system control panel
+QString LOS::AppStoreShortcut(){ return ""; } //graphical app/pkg manager
+
+// ==== ExternalDevicePaths() ====
+QStringList LOS::ExternalDevicePaths(){
+ //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+
+ //Not implemented yet
+ return QStringList();
+}
+
+//Read screen brightness information
+int LOS::ScreenBrightness(){
+ //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ return -1; //not implemented yet
+}
+
+//Set screen brightness
+void LOS::setScreenBrightness(int percent){
+ //not implemented yet
+}
+
+//Read the current volume
+int LOS::audioVolume(){
+ //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ return -1; //Not implemented yet
+}
+
+//Set the current volume
+void LOS::setAudioVolume(int percent){
+ //not implemented yet
+}
+
+//Change the current volume a set amount (+ or -)
+void LOS::changeAudioVolume(int percentdiff){
+ //not implemented yet
+}
+
+//Check if a graphical audio mixer is installed
+bool LOS::hasMixerUtility(){
+ return false; //not implemented yet
+}
+
+//Launch the graphical audio mixer utility
+void LOS::startMixerUtility(){
+ //not implemented yet
+}
+
+//Check for user system permission (shutdown/restart)
+bool LOS::userHasShutdownAccess(){
+ return false; //not implemented yet
+}
+
+//Check for whether the system is safe to power off (no updates being perfomed)
+bool LOS::systemPerformingUpdates(){
+ return false; //Not implemented yet
+}
+
+//System Shutdown
+void LOS::systemShutdown(){ //start poweroff sequence
+ QProcess::startDetached("shutdown -p now");
+}
+
+//System Restart
+void LOS::systemRestart(){ //start reboot sequence
+ QProcess::startDetached("shutdown -r now");
+}
+
+//Check for suspend support
+bool LOS::systemCanSuspend(){
+ return false;
+}
+
+//Put the system into the suspend state
+void LOS::systemSuspend(){
+
+}
+
+//Battery Availability
+bool LOS::hasBattery(){
+ return false; //not implemented yet
+}
+
+//Battery Charge Level
+int LOS::batteryCharge(){ //Returns: percent charge (0-100), anything outside that range is counted as an error
+ return -1; //not implemented yet
+}
+
+//Battery Charging State
+bool LOS::batteryIsCharging(){
+ return false; //not implemented yet
+}
+
+//Battery Time Remaining
+int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining
+ return 0; //not implemented yet
+}
+
+//File Checksums
+QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file
+ return QStringList();
+}
+
+//file system capacity
+QString LOS::FileSystemCapacity(QString dir) { //Return: percentage capacity as give by the df command
+ return QString();
+}
+
+QStringList LOS::CPUTemperatures(){ //Returns: List containing the temperature of any CPU's ("50C" for example)
+ return QStringList(); //not implemented yet
+}
+
+int LOS::CPUUsagePercent(){ //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ return -1; //not implemented yet
+}
+
+int LOS::MemoryUsagePercent(){
+ return -1; //not implemented yet
+}
+
+QStringList LOS::DiskUsage(){ //Returns: List of current read/write stats for each device
+ return QStringList(); //not implemented yet
+}
+#endif
diff --git a/src-qt5/core/libLumina/LuminaOS.h b/src-qt5/core/libLumina/LuminaOS.h
new file mode 100644
index 00000000..c305277a
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaOS.h
@@ -0,0 +1,93 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-15, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the main interface for any OS-specific system calls
+// To port Lumina to a different operating system, just create a file
+// called "LuminaOS-<Operating System>.cpp", and use that file in
+// the project (libLumina.pro) instead of LuminaOS-FreeBSD.cpp
+//===========================================
+#ifndef _LUMINA_LIBRARY_OS_H
+#define _LUMINA_LIBRARY_OS_H
+
+#include <QString>
+#include <QStringList>
+#include <QProcess>
+#include <QDir>
+#include <QObject>
+
+#include "LuminaUtils.h"
+
+class LOS{
+public:
+ //Return the name of the OS being used
+ static QString OSName();
+
+ //OS-specific prefix(s)
+ static QString LuminaShare(); //Install dir for Lumina share files
+ static QString AppPrefix(); //Prefix for applications (/usr/local/ on FreeBSD)
+ static QString SysPrefix(); //Prefix for system (/usr/ on FreeBSD)
+
+ //OS-specific application shortcuts (*.desktop files)
+ static QString ControlPanelShortcut();
+ static QString AppStoreShortcut();
+
+ //Scan for mounted external devices
+ static QStringList ExternalDevicePaths(); //Returns: QStringList[<type>::::<filesystem>::::<path>]
+ //Note: <type> = [USB, HDRIVE, DVD, SDCARD, UNKNOWN]
+
+ //Read screen brightness information
+ static int ScreenBrightness(); //Returns: Screen Brightness as a percentage (0-100, with -1 for errors)
+ //Set screen brightness
+ static void setScreenBrightness(int percent);
+
+ //Read the current volume
+ static int audioVolume(); //Returns: audio volume as a percentage (0-100, with -1 for errors)
+ //Set the current volume
+ static void setAudioVolume(int percent);
+ //Modify the current volume by a set amount (+ or -)
+ static void changeAudioVolume(int percentdiff);
+
+ //Check if a graphical audio mixer is installed
+ static bool hasMixerUtility();
+ //Launch the graphical audio mixer utility
+ static void startMixerUtility();
+
+ //Check for user system permission (shutdown/restart)
+ static bool userHasShutdownAccess();
+ static bool systemPerformingUpdates();
+ //System Shutdown
+ static void systemShutdown(); //start poweroff sequence
+ //System Restart
+ static void systemRestart(); //start reboot sequence
+ //Check for suspend support
+ static bool systemCanSuspend();
+ //Put the system into the suspend state
+ static void systemSuspend();
+
+
+ //Battery Availability
+ static bool hasBattery();
+ //Battery Charge Level
+ static int batteryCharge(); //Returns: percent charge (0-100), anything outside that range is counted as an error
+ //Battery Charging State
+ static bool batteryIsCharging();
+ //Battery Time Remaining
+ static int batterySecondsLeft(); //Returns: estimated number of seconds remaining
+
+ //Get the checksum for a file
+ static QStringList Checksums(QStringList filepaths); //Return: checksum of each input file (same order)
+
+ //Get the filesystem capacity
+ static QString FileSystemCapacity(QString dir) ; //Return: percentage capacity as give by the df command
+
+ //System CPU Information
+ static QStringList CPUTemperatures(); //Returns: List containing the temperature of any CPU's ("50C" for example)
+ static int CPUUsagePercent(); //Returns: Overall percentage of the amount of CPU cycles in use (-1 for errors)
+ static int MemoryUsagePercent(); //Returns: Overall percentage of the amount of available memory in use (-1 for errors)
+ static QStringList DiskUsage(); //Returns: List of current read/write stats for each device
+};
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaSingleApplication.cpp b/src-qt5/core/libLumina/LuminaSingleApplication.cpp
new file mode 100644
index 00000000..30507b5e
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaSingleApplication.cpp
@@ -0,0 +1,137 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LuminaSingleApplication.h"
+#include <QDir>
+#include <QFile>
+#include <QLocalSocket>
+#include <QDebug>
+#include <QX11Info>
+
+#include <unistd.h> //for getlogin()
+
+LSingleApplication::LSingleApplication(int &argc, char **argv, QString appname) : QApplication(argc, argv){
+ //Load the proper translation systems
+ if(appname!="lumina-desktop"){ cTrans = LUtils::LoadTranslation(this, appname); }//save the translator for later
+ //Initialize a couple convenience internal variables
+ cfile = QDir::tempPath()+"/.LSingleApp-%1-%2-%3";
+ QString username = QString(getlogin());
+ //For locking the process use the official process name - not the user input (no masking)
+ appname = this->applicationName();
+ cfile = cfile.arg( username, appname, QString::number(QX11Info::appScreen()) );
+ lockfile = new QLockFile(cfile+"-lock");
+ lockfile->setStaleLockTime(0); //long-lived processes
+ for(int i=1; i<argc; i++){
+ QString path = QString::fromLocal8Bit(argv[i]);
+ //do few quick conversions for relative paths and such as necessary
+ // (Remember: this is only used for secondary processes, not the primary)
+ if(path=="."){
+ //Insert the current working directory instead
+ path = QDir::currentPath();
+ }else{
+ if(!path.startsWith("/") && !path.startsWith("-") ){ path.prepend(QDir::currentPath()+"/"); }
+ }
+ inputlist << path;
+ }
+ isActive = isBypass = false;
+ lserver = 0;
+ //Now check for the manual CLI flag to bypass single-instance forwarding (if necessary)
+ if(inputlist.contains("-new-instance")){
+ isBypass = true;
+ inputlist.removeAll("-new-instance");
+ }
+ PerformLockChecks();
+}
+
+LSingleApplication::~LSingleApplication(){
+ if(lserver != 0 && lockfile->isLocked() ){
+ //currently locked instance: remove the lock now
+ lserver->close();
+ QLocalServer::removeServer(cfile);
+ lockfile->unlock();
+ }
+}
+
+bool LSingleApplication::isPrimaryProcess(){
+ return (isActive || isBypass);
+}
+
+void LSingleApplication::PerformLockChecks(){
+ bool primary = lockfile->tryLock();
+ //qDebug() << "Try Lock: " << primary;
+ if(!primary){
+ //Pre-existing lock - check it for validity
+ QString appname, hostname;
+ qint64 pid;
+ lockfile->getLockInfo(&pid, &hostname, &appname); //PID already exists if it gets this far, ignore hostname
+ //qDebug() << " - Lock Info:" << pid << hostname << appname;
+ if( appname!=this->applicationName() || !QFile::exists(cfile) ){
+ //Some other process has the same PID or the server does not exist - stale lock
+ qDebug() << " - Cleaning stale single-instance lock:";
+ if(lockfile->removeStaleLockFile() ){
+ if(QFile::exists(cfile)){ QLocalServer::removeServer(cfile); } //also remove stale socket/server file
+ }else{
+ qDebug() << " -- Could not remove lock file";
+ }
+ //Now re-try to create the lock
+ primary = lockfile->tryLock();
+ //qDebug() << " - Try Lock Again:" << primary;
+ }
+ }
+ if(primary || !QFile::exists(cfile) ){
+ //Create the server socket
+ //qDebug() << "Create Local Server";
+ if(QFile::exists(cfile)){ QLocalServer::removeServer(cfile); } //stale socket/server file
+ lserver = new QLocalServer(this);
+ connect(lserver, SIGNAL(newConnection()), this, SLOT(newInputsAvailable()) );
+ if( lserver->listen(cfile) ){
+ qDebug() << " - Created new single-instance lock";
+ lserver->setSocketOptions(QLocalServer::UserAccessOption);
+ //qDebug() << " - Success";
+ isActive = true;
+ }else{
+ qDebug() << " - WARNING: Could not create single-instance framework";
+ qDebug() << " - Falling back on standard application startup";
+ lockfile->unlock();
+ isActive = true;
+ }
+
+ }else if(!isBypass){
+ //forward the current inputs to the locked process for processing and exit
+ //Check the connection to the local server first
+ qDebug() << "Single-instance lock found";
+ QLocalSocket socket(this);
+ socket.connectToServer(cfile);
+ socket.waitForConnected();
+ if(!socket.isValid() || socket.state()!=QLocalSocket::ConnectedState){
+ //error - could not forward info for some reason
+ qDebug() << " - Could not connect to locking process: exiting...";
+ exit(1);
+ }
+
+ qDebug() << " - Forwarding inputs to locking process and closing down this instance...";
+ socket.write( inputlist.join("::::").toLocal8Bit() );
+ socket.waitForDisconnected(500); //max out at 1/2 second (only hits this if no inputs)
+ }
+
+}
+
+//New messages detected
+void LSingleApplication::newInputsAvailable(){
+ while(lserver->hasPendingConnections()){
+ QLocalSocket *sock = lserver->nextPendingConnection();
+ QByteArray bytes;
+ sock->waitForReadyRead();
+ while(sock->bytesAvailable() > 0){ //if(sock->waitForReadyRead()){
+ //qDebug() << "Info Available";
+ bytes.append( sock->readAll() );
+ }
+ sock->disconnectFromServer();
+ QStringList inputs = QString::fromLocal8Bit(bytes).split("::::");
+ //qDebug() << " - New Inputs Detected:" << inputs;
+ emit InputsAvailable(inputs);
+ }
+} \ No newline at end of file
diff --git a/src-qt5/core/libLumina/LuminaSingleApplication.h b/src-qt5/core/libLumina/LuminaSingleApplication.h
new file mode 100644
index 00000000..725d8e40
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaSingleApplication.h
@@ -0,0 +1,59 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the general class for a single-instance application
+//===========================================
+//EXAMPLE USAGE in main.cpp:
+//
+// LSingleApplication app(argc, argv);
+// if( !app.isPrimaryProcess() ){
+// return 0;
+// }
+// QMainWindow w; //or whatever the main window class is
+// connect(app, SIGNAL(InputsAvailable(QStringList)), w, SLOT(<some slot>)); //for interactive apps - optional
+// app.exec();
+//===========================================
+#ifndef _LUMINA_LIBRARY_SINGLE_APPLICATION_H
+#define _LUMINA_LIBRARY_SINGLE_APPLICATION_H
+
+#include <QString>
+#include <QStringList>
+#include <QLocalServer>
+#include <QLockFile>
+#include <QApplication>
+
+#include <LuminaUtils.h>
+
+//NOTE: This application type will automatically load the proper translation file(s)
+// if the application name is set properly
+class LSingleApplication : public QApplication{
+ Q_OBJECT
+public:
+ LSingleApplication(int &argc, char **argv, QString appname);
+ ~LSingleApplication();
+
+ bool isPrimaryProcess();
+
+ QStringList inputlist; //in case the app wants access to modified inputs (relative path fixes and such)
+
+private:
+ bool isActive, isBypass;
+ QLockFile *lockfile;
+ QLocalServer *lserver;
+ QString cfile;
+ QTranslator *cTrans; //current translation
+
+ void PerformLockChecks();
+
+private slots:
+ void newInputsAvailable(); //internally used to detect a message from an alternate instance
+
+signals:
+ void InputsAvailable(QStringList);
+
+};
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaThemes.cpp b/src-qt5/core/libLumina/LuminaThemes.cpp
new file mode 100644
index 00000000..415b3acf
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaThemes.cpp
@@ -0,0 +1,503 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LuminaThemes.h"
+
+#include "LuminaUtils.h"
+#include "LuminaOS.h"
+#include <QIcon>
+#include <QFont>
+#include <QDebug>
+#include <QObject>
+#include <QPainter>
+#include <QPen>
+
+#include <unistd.h>
+
+//Stuff necesary for Qt Cursor Reloads
+//#include "qxcbcursor.h" //needed to prod Qt to refresh the mouse cursor theme
+//#include <QCursor>
+
+QStringList LTHEME::availableSystemThemes(){
+ //returns: [name::::path] for each item
+ QDir dir(LOS::LuminaShare()+"themes");
+ QStringList list = dir.entryList(QStringList() <<"*.qss.template", QDir::Files, QDir::Name);
+ for(int i=0; i<list.length(); i++){
+ //Format the output entry [<name>::::<fullpath>]
+ list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
+ }
+ return list;
+}
+
+QStringList LTHEME::availableLocalThemes(){ //returns: [name::::path] for each item
+ QDir dir(QDir::homePath()+"/.lumina/themes");
+ QStringList list = dir.entryList(QStringList() <<"*.qss.template", QDir::Files, QDir::Name);
+ for(int i=0; i<list.length(); i++){
+ //Format the output entry [<name>::::<fullpath>]
+ list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
+ }
+ return list;
+}
+
+QStringList LTHEME::availableSystemColors(){ //returns: [name::::path] for each item
+ //returns: [name::::path] for each item
+ QDir dir(LOS::LuminaShare()+"colors");
+ QStringList list = dir.entryList(QStringList() <<"*.qss.colors", QDir::Files, QDir::Name);
+ for(int i=0; i<list.length(); i++){
+ //Format the output entry [<name>::::<fullpath>]
+ list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
+ }
+ return list;
+}
+
+QStringList LTHEME::availableLocalColors(){ //returns: [name::::path] for each item
+ QDir dir(QDir::homePath()+"/.lumina/colors");
+ QStringList list = dir.entryList(QStringList() <<"*.qss.colors", QDir::Files, QDir::Name);
+ for(int i=0; i<list.length(); i++){
+ //Format the output entry [<name>::::<fullpath>]
+ list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
+ }
+ return list;
+}
+
+QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item
+ QStringList paths;
+ paths << QDir::homePath()+"/.icons";
+ QStringList xdd = QString(getenv("XDG_DATA_HOME")).split(":");
+ xdd << QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int i=0; i<xdd.length(); i++){
+ if(QFile::exists(xdd[i]+"/icons")){
+ paths << xdd[i]+"/icons";
+ }
+ }
+ //Now get all the icon themes in these directories
+ QStringList themes, tmpthemes;
+ QDir dir;
+ for(int i=0; i<paths.length(); i++){
+ if(dir.cd(paths[i])){
+ tmpthemes = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for(int j=0; j<tmpthemes.length(); j++){
+ if(tmpthemes[j].startsWith("default")){ continue; }
+ if(QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/index.theme")) ){ themes << tmpthemes[j]; }
+ }
+ }
+ }
+ themes.removeDuplicates();
+ themes.sort();
+ return themes;
+}
+
+QStringList LTHEME::availableSystemCursors(){ //returns: [name] for each item
+ QStringList paths; paths << LOS::SysPrefix()+"lib/X11/icons/" << LOS::AppPrefix()+"lib/X11/icons/";
+ QStringList out;
+ for(int i=0; i<paths.length(); i++){
+ if( !QFile::exists(paths[i]) ){ continue; }
+ QDir dir(paths[i]);
+ QStringList tmp = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for(int j=0; j<tmp.length(); j++){
+ if(QFile::exists(paths[i]+tmp[j]+"/cursors")){
+ out << tmp[j]; //good theme - save it to the output list
+ }
+ }
+ }
+ return out;
+}
+
+//Save a new theme/color file
+bool LTHEME::saveLocalTheme(QString name, QStringList contents){
+ QString localdir = QDir::homePath()+"/.lumina/themes/";
+ if(!QFile::exists(localdir)){ QDir dir; dir.mkpath(localdir); }
+ return LUtils::writeFile(localdir+name+".qss.template", contents, true);
+}
+
+bool LTHEME::saveLocalColors(QString name, QStringList contents){
+ QString localdir = QDir::homePath()+"/.lumina/colors/";
+ if(!QFile::exists(localdir)){ QDir dir; dir.mkpath(localdir); }
+ return LUtils::writeFile(localdir+name+".qss.colors", contents, true);
+}
+
+//Return the currently selected Theme/Colors/Icons
+QStringList LTHEME::currentSettings(){ //returns [theme path, colorspath, iconsname, font, fontsize]
+ QStringList out; out << "" << "" << "" << "" << "";
+ QStringList settings = LUtils::readFile(QDir::homePath()+"/.lumina/themesettings.cfg");
+ for(int i=0; i<settings.length(); i++){
+ if(settings[i].startsWith("THEMEFILE=")){ out[0] = settings[i].section("=",1,1).simplified(); }
+ else if(settings[i].startsWith("COLORFILE=")){ out[1] = settings[i].section("=",1,1).simplified(); }
+ else if(settings[i].startsWith("ICONTHEME=")){ out[2] = settings[i].section("=",1,1).simplified(); }
+ else if(settings[i].startsWith("FONTFAMILY=")){ out[3] = settings[i].section("=",1,1).simplified(); }
+ else if(settings[i].startsWith("FONTSIZE=")){ out[4] = settings[i].section("=",1,1).simplified(); }
+ }
+ bool nofile = settings.isEmpty();
+ if(out[0].isEmpty() || !QFile::exists(out[0]) ){ out[0] = LOS::LuminaShare()+"themes/Lumina-default.qss.template"; }
+ if(out[1].isEmpty() || !QFile::exists(out[1]) ){ out[1] = LOS::LuminaShare()+"colors/Lumina-Glass.qss.colors"; }
+ if(out[3].isEmpty()){ out[3] = QFont().defaultFamily(); }
+ if(out[4].isEmpty()){
+ int num = QFont().pointSize(); out[4] = QString::number(num)+"pt"; //Check point size first
+ if(num<0){ num = QFont().pixelSize(); out[4] = QString::number(num)+"px";} //Now check pixel size
+ if(num<0){ out[4] = "9pt"; } //Now hard-code a fallback (just in case)
+ }
+ if(nofile){ setCurrentSettings(out[0], out[1], out[2], out[3], out[4]); }
+
+ return out;
+}
+
+//Return the currently-selected Cursor theme
+QString LTHEME::currentCursor(){
+ //qDebug() << "Reading Current Cursor Theme:";
+ QStringList info = LUtils::readFile(QDir::homePath()+"/.icons/default/index.theme");
+ if(info.isEmpty()){ return ""; }
+ QString cursor;
+ bool insection = false;
+ for(int i=0; i<info.length(); i++){
+ if(info[i]=="[Icon Theme]"){ insection = true; continue;}
+ else if(insection && info[i].startsWith("Inherits=")){
+ cursor = info[i].section("=",1,1).simplified();
+ break;
+ }
+ }
+ //qDebug() << " - found theme:" << cursor;
+ return cursor;
+}
+
+ //Change the current Theme/Colors/Icons
+bool LTHEME::setCurrentSettings(QString themepath, QString colorpath, QString iconname, QString font, QString fontsize){
+ QIcon::setThemeName(iconname);
+ //Now save the theme settings file
+ QStringList contents;
+ contents << "THEMEFILE="+themepath;
+ contents << "COLORFILE="+colorpath;
+ contents << "ICONTHEME="+iconname;
+ contents << "FONTFAMILY="+font;
+ contents << "FONTSIZE="+fontsize;
+ bool ok = LUtils::writeFile(QDir::homePath()+"/.lumina/themesettings.cfg", contents, true);
+
+ return ok;
+}
+
+//Change the current Cursor Theme
+bool LTHEME::setCursorTheme(QString cursorname){
+//qDebug() << "Set Cursor Theme:" << cursorname;
+ QStringList info = LUtils::readFile(QDir::homePath()+"/.icons/default/index.theme");
+ bool insection = false;
+ bool changed = false;
+ QString newval = "Inherits="+cursorname;
+ for(int i=0; i<info.length() && !changed; i++){
+ if(info[i]=="[Icon Theme]"){
+ insection = true;
+ }else if( info[i].startsWith("[") && insection){
+ //Section does not have the setting - add it
+ info.insert(i, newval);
+ changed =true;
+ }else if( info[i].startsWith("[") ){
+ insection = false;
+ }else if(insection && info[i].startsWith("Inherits=")){
+ info[i] = newval; //replace the current setting
+ changed = true;
+ }
+ } //end loop over file contents
+ if(!changed){ //Could not change the file contents for some reason
+ if(insection){ info << newval; } //end of file while in the section
+ else{ info << "[Icon Theme]" << newval; } //entire section missing from file
+ }
+ //Now save the file
+ //qDebug() << "Done saving the cursor:" << info;
+ return LUtils::writeFile(QDir::homePath()+"/.icons/default/index.theme", info, true);
+}
+
+ //Return the complete stylesheet for a given theme/colors
+QString LTHEME::assembleStyleSheet(QString themepath, QString colorpath, QString font, QString fontsize){
+ QString stylesheet = LUtils::readFile(themepath).join("\n");
+ QStringList colors = LUtils::readFile(colorpath);
+ //qDebug() << "Found Theme:" << themepath << stylesheet;
+ //qDebug() << "Found Colors:" << colorpath << colors;
+ QStringList systhemes = availableSystemThemes();
+ QStringList locthemes = availableLocalThemes();
+ //Now do any inheritance between themes
+ int start = stylesheet.indexOf("INHERITS=");
+ while(start>=0){
+ QString line = stylesheet.mid(start, stylesheet.indexOf("\n",start)-start); //only get this line
+ QString inherit = line.section("=",1,1);
+ QString rStyle; //replacement stylesheet
+ if(!locthemes.filter(inherit+"::::").isEmpty()){
+ rStyle = LUtils::readFile(locthemes.filter(inherit+"::::").first().section("::::",1,1)).join("\n");
+ }else if(!systhemes.filter(inherit+"::::").isEmpty()){
+ rStyle = LUtils::readFile(systhemes.filter(inherit+"::::").first().section("::::",1,1)).join("\n");
+ }
+ stylesheet.replace(line, rStyle);
+ //Now look for the next one
+ start = stylesheet.indexOf("INHERITS=");
+ }
+ //Now perform the color replacements
+ for(int i=0; i<colors.length(); i++){
+ if(colors[i].isEmpty() || colors[i].startsWith("#")){ continue; }
+ else if(colors[i].startsWith("PRIMARYCOLOR=")){ stylesheet = stylesheet.replace("%%PRIMARYCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("SECONDARYCOLOR=")){ stylesheet = stylesheet.replace("%%SECONDARYCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("HIGHLIGHTCOLOR=")){ stylesheet = stylesheet.replace("%%HIGHLIGHTCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("ACCENTCOLOR=")){ stylesheet = stylesheet.replace("%%ACCENTCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("PRIMARYDISABLECOLOR=")){ stylesheet = stylesheet.replace("%%PRIMARYDISABLECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("SECONDARYDISABLECOLOR=")){ stylesheet = stylesheet.replace("%%SECONDARYDISABLECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("HIGHLIGHTDISABLECOLOR=")){ stylesheet = stylesheet.replace("%%HIGHLIGHTDISABLECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("ACCENTDISABLECOLOR=")){ stylesheet = stylesheet.replace("%%ACCENTDISABLECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("BASECOLOR=")){ stylesheet = stylesheet.replace("%%BASECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("ALTBASECOLOR=")){ stylesheet = stylesheet.replace("%%ALTBASECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("TEXTCOLOR=")){ stylesheet = stylesheet.replace("%%TEXTCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("TEXTDISABLECOLOR=")){ stylesheet = stylesheet.replace("%%TEXTDISABLECOLOR%%", colors[i].section("=",1,1).simplified()); }
+ else if(colors[i].startsWith("TEXTHIGHLIGHTCOLOR=")){ stylesheet = stylesheet.replace("%%TEXTHIGHLIGHTCOLOR%%", colors[i].section("=",1,1).simplified()); }
+ }
+ stylesheet = stylesheet.replace("%%FONT%%", "\""+font+"\"");
+ stylesheet = stylesheet.replace("%%FONTSIZE%%", fontsize);
+ //qDebug() << "Assembled Style Sheet:\n" << stylesheet;
+ return stylesheet;
+}
+// Extra information about a cursor theme
+QStringList LTHEME::cursorInformation(QString name){
+ //returns: [Name, Comment, Sample Image File]
+ QStringList out; out << "" << "" << ""; //ensure consistent output structure
+ QStringList paths; paths << LOS::SysPrefix()+"lib/X11/icons/" << LOS::AppPrefix()+"lib/X11/icons/";
+ for(int i=0; i<paths.length(); i++){
+ if(QFile::exists(paths[i]+name)){
+ if(QFile::exists(paths[i]+name+"/cursors/arrow")){ out[2] = paths[i]+name+"/cursors/arrow"; }
+ QStringList info = LUtils::readFile(paths[i]+name+"/index.theme");
+ for(int j=info.indexOf("[Icon Theme]"); j<info.length(); j++){
+ if(j<0){continue; } //just in case the index function errors out
+ if(info[j].startsWith("Name") && info[j].contains("=")){ out[0] = info[j].section("=",1,1).simplified(); }
+ else if(info[j].startsWith("Comment") && info[j].contains("=")){ out[1] = info[j].section("=",1,1).simplified(); }
+ }
+ break; //found the cursor
+ }
+ }
+ return out;
+}
+
+QStringList LTHEME::CustomEnvSettings(){ //view all the key=value settings
+ static QStringList info = QStringList();
+ static QDateTime lastcheck = QDateTime();
+ if(lastcheck.isNull() || lastcheck < QFileInfo(QDir::homePath()+"/.lumina/envsettings.conf").lastModified()){
+ lastcheck = QDateTime::currentDateTime();
+ info = LUtils::readFile(QDir::homePath()+"/.lumina/envsettings.conf");
+ }
+ return info;
+}
+
+void LTHEME::LoadCustomEnvSettings(){
+ //will push the custom settings into the environment (recommended before loading the initial QApplication)
+ QStringList info = LTHEME::CustomEnvSettings();
+ if(info.isEmpty()){
+ //Ensure the file exists, and create it otherwise;
+ if(!QFile::exists(QDir::homePath()+"/.lumina/envsettings.conf")){
+ LUtils::writeFile(QDir::homePath()+"/.lumina/envsettings.conf", QStringList() << "", true);
+ }
+ }
+ for(int i=0; i<info.length(); i++){
+ if(info[i].isEmpty()){ continue; }
+ if(info[i].section("=",1,100).isEmpty()){
+ unsetenv(info[i].section("=",0,0).toLocal8Bit());
+ }else{
+ setenv(info[i].section("=",0,0).toLocal8Bit(), info[i].section("=",1,100).simplified().toLocal8Bit(), 1);
+ }
+ }
+
+}
+
+bool LTHEME::setCustomEnvSetting(QString var, QString val){
+ //variable/value pair (use an empty val to clear it)
+ QStringList info = LTHEME::CustomEnvSettings();
+ bool changed = false;
+ if(!info.filter(var+"=").isEmpty()){
+ for(int i=0; i<info.length(); i++){
+ //Make sure this is an exact variable match
+ if(!info[i].startsWith(var+"=")){ continue; }
+ //Found it - replace this line
+ info[i] = var+"="+val;
+ changed = true;
+ }
+ }
+ if(!changed){ info << var+"="+val; }
+ return LUtils::writeFile(QDir::homePath()+"/.lumina/envsettings.conf", info, true);
+}
+
+QString LTHEME::readCustomEnvSetting(QString var){
+ QStringList info = LTHEME::CustomEnvSettings().filter(var+"=");
+ for(int i=0; i<info.length(); i++){
+ if(info[i].startsWith(var+"=")){
+ return info[i].section("=",1,100).simplified();
+ }
+ }
+ //If it gets here, no setting found for that variable
+ return "";
+}
+
+// =========================
+// LuminaThemeStyle
+// =========================
+/*LuminaThemeStyle::LuminaThemeStyle() : QProxyStyle(){
+ this->update();
+}
+
+LuminaThemeStyle::~LuminaThemeStyle(){
+
+}
+
+//Function to update the style (for use by the theme engine)
+void LuminaThemeStyle::update(){
+ darkfont = true; //make this dynamic later
+}*/
+
+//Subclassed functions
+//void LuminaThemeStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole) const{
+ /*QFont cfont = painter->font();
+ cfont.setHintingPreference(QFont::PreferFullHinting);
+ QFont outfont = cfont;
+ outfont.setStretch(101);
+ outfont.setLetterSpacing(QFont::PercentageSpacing, 99);
+ //Paint the background outline
+ if(darkfont){ painter->setPen(QPen(Qt::white)); }
+ else{ painter->setPen(QPen(Qt::black)); }
+ painter->setFont(outfont);
+ //QRect outline = QRect(rect.left()+2, rect.top()+2, rect.right()+2, rect.bottom()+2);
+ painter->drawText(rect, text);
+
+ //Paint the text itself (Make this respect the "enabled" flag later)
+ painter->setFont(cfont);
+ if(darkfont){ painter->setPen(QPen(Qt::black)); }
+ else{ painter->setPen(QPen(Qt::white)); }
+ painter->drawText(rect, text);*/
+
+ /*QFont font = painter->font();
+ QFont cfont = font; //save for later
+ if(font.pixelSize()>0){ font.setPixelSize( font.pixelSize()-4); }
+ else{ font.setPointSize(font.pointSize()-1); }
+ painter->setFont(font);
+ //Create the path
+ QPainterPath path;
+ //path.setFillRule(Qt::WindingFill);
+ path.addText(rect.left(), rect.center().y()+(painter->fontMetrics().xHeight()/2), painter->font(), text);
+ //Now set the border/fill colors
+ QPen pen;
+ pen.setWidth(2);
+ if(darkfont){
+ pen.setColor(Qt::white);
+ painter->fillPath(path,Qt::black);
+ }else{
+ pen.setColor(Qt::black);
+ painter->fillPath(path,Qt::white);
+ }
+ painter->setPen(pen);
+ painter->drawPath(path);
+ painter->setFont(cfont); //reset back to original font*/
+
+//}
+
+
+//==================
+// THEME ENGINE CLASS
+//==================
+LuminaThemeEngine::LuminaThemeEngine(QApplication *app){
+ application=app; //save this pointer for later
+ //style = new LuminaThemeStyle();
+ //Set the application-wide style
+ //application->setStyle( style );
+
+ lastcheck = QDateTime::currentDateTime(); //
+ // Now load the theme stylesheet
+ QStringList current = LTHEME::currentSettings();
+ theme = current[0]; colors=current[1]; icons=current[2]; font=current[3]; fontsize=current[4];
+ cursors = LTHEME::currentCursor();
+ application->setStyleSheet( LTHEME::assembleStyleSheet(theme, colors, font, fontsize) );
+ //Make sure to prefer font antialiasing on the application
+ /*QFont tmp = application->font();
+ tmp.setStyleStrategy(QFont::PreferOutline);
+ tmp.setFamily(font);
+ tmp.setHintingPreference(QFont::PreferFullHinting);
+ if(fontsize.endsWith("pt")){ tmp.setPointSize(fontsize.section("pt",0,0).toInt()); }
+ else if(fontsize.endsWith("px")){ tmp.setPixelSize(fontsize.section("px",0,0).toInt()); }
+ application->setFont(tmp);*/
+ QIcon::setThemeName(icons); //make sure this sets set within this environment
+ syncTimer = new QTimer(this);
+ syncTimer->setSingleShot(true);
+ syncTimer->setInterval(500); //wait 1/2 second before re-loading the files
+ if(cursors.isEmpty()){
+ LTHEME::setCursorTheme("default"); //X11 fallback (always installed?)
+ cursors = "default";
+ }
+
+ //setenv("XCURSOR_THEME", cursors.toLocal8Bit(),1);
+ watcher = new QFileSystemWatcher(this);
+ watcher->addPath( QDir::homePath()+"/.lumina/envsettings.conf" );
+ watcher->addPath( QDir::homePath()+"/.lumina/themesettings.cfg" );
+ watcher->addPaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" ); //also watch these files for changes
+ connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange(QString)) );
+ connect(syncTimer, SIGNAL(timeout()), this, SLOT(reloadFiles()) );
+}
+
+LuminaThemeEngine::~LuminaThemeEngine(){
+
+}
+
+void LuminaThemeEngine::refresh(){
+ QTimer::singleShot(100,this, SLOT(reloadFiles()) );
+}
+
+void LuminaThemeEngine::watcherChange(QString file){
+ if(syncTimer->isActive()){ syncTimer->stop(); }
+ syncTimer->start();
+ if(!watcher->files().contains(file)){ watcher->addPath(file); }
+}
+
+void LuminaThemeEngine::reloadFiles(){
+ //Check the Theme file/settings
+ if(lastcheck < QFileInfo(QDir::homePath()+"/.lumina/themesettings.cfg").lastModified().addSecs(1) ){
+ QStringList current = LTHEME::currentSettings();
+ application->setStyleSheet( LTHEME::assembleStyleSheet(current[0], current[1], current[3], current[4]) );
+ if(icons!=current[2]){
+ QIcon::setThemeName(current[2]); //make sure this sets set within this environment
+ emit updateIcons();
+ }
+ //save the settings for comparison later
+ theme = current[0]; colors=current[1]; icons=current[2];
+
+ if(font!=current[3] || fontsize!=current[4]){
+ font=current[3]; fontsize=current[4];
+ QFont tmp = application->font();
+ tmp.setStyleStrategy(QFont::PreferAntialias);
+ tmp.setFamily(font);
+ if(fontsize.endsWith("pt")){ tmp.setPointSize(fontsize.section("pt",0,0).toInt()); }
+ else if(fontsize.endsWith("px")){ tmp.setPixelSize(fontsize.section("px",0,0).toInt()); }
+ application->setFont(tmp);
+ }
+ }
+ //Check the Cursor file/settings
+ if(lastcheck < QFileInfo(QDir::homePath()+"/.icons/default/index.theme").lastModified()){
+ QString ccurs = LTHEME::currentCursor();
+ if(cursors != ccurs){
+ emit updateCursors();
+ //Might be something we can do automatically here as well - since we have the QApplication handy
+ // - Note: setting/unsetting an override cursor does not update the current cursor bitmap
+ // Qt created a background database/hash/mapping of the theme pixmaps on startup
+ // So Qt itself needs to be prodded to update that mapping
+ /*QXcbCursor::cursorThemePropertyChanged( \
+ new QXcbVirtualDesktop(QX11Info::connection(), application->screen()->handle(), QX11Info::appScreen()),
+ ccurs.toData(), QVariant("Inherits"), NULL);*/
+ //QCursorData::cleanup();
+ //QCursorData::initialize();
+ //setenv("XCURSOR_THEME", ccurs.toLocal8Bit(),1);
+ }
+ cursors = ccurs;
+ }
+
+
+ //Environment Changes
+ if( lastcheck < QFileInfo(QDir::homePath()+"/.lumina/envsettings.conf").lastModified()){
+ LTHEME::LoadCustomEnvSettings();
+ emit EnvChanged();
+ }
+ lastcheck = QDateTime::currentDateTime();
+
+ //Now update the watched files to ensure nothing is missed
+ watcher->removePaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" << QDir::homePath()+"/.lumina/envsettings.conf");
+ watcher->addPaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" << QDir::homePath()+"/.lumina/envsettings.conf");
+}
+
diff --git a/src-qt5/core/libLumina/LuminaThemes.h b/src-qt5/core/libLumina/LuminaThemes.h
new file mode 100644
index 00000000..f0b8c2b6
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaThemes.h
@@ -0,0 +1,115 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This class governs all the stylesheet usage and interactions
+// for the Lumina utilities to provide a consistant theme for the system
+//===========================================
+#ifndef _LUMINA_LIBRARY_THEMES_H
+#define _LUMINA_LIBRARY_THEMES_H
+
+#include <QApplication>
+#include <QObject>
+#include <QFileSystemWatcher>
+#include <QString>
+#include <QFile>
+#include <QDir>
+#include <QTimer>
+#include <QDateTime>
+#include <QStyle>
+#include <QProxyStyle>
+
+class LTHEME{
+public:
+ //Read the Themes/Colors/Icons that are available on the system
+ static QStringList availableSystemThemes();//returns: [name::::path] for each item
+ static QStringList availableLocalThemes(); //returns: [name::::path] for each item
+ static QStringList availableSystemColors(); //returns: [name::::path] for each item
+ static QStringList availableLocalColors(); //returns: [name::::path] for each item
+ static QStringList availableSystemIcons(); //returns: [name] for each item
+ static QStringList availableSystemCursors(); //returns: [name] for each item
+
+ //Save a new theme/color file
+ static bool saveLocalTheme(QString name, QStringList contents);
+ static bool saveLocalColors(QString name, QStringList contents);
+
+ //Return the currently selected Theme/Colors/Icons
+ static QStringList currentSettings(); //returns [theme path, colorspath, iconsname, font, fontsize]
+ static QString currentCursor(); //returns: current cursor theme name
+
+ //Change the current Theme/Colors/Icons
+ static bool setCurrentSettings(QString themepath, QString colorpath, QString iconname, QString font, QString fontsize);
+ static bool setCursorTheme(QString cursorname);
+
+ //Return the complete stylesheet for a given theme/colors
+ static QString assembleStyleSheet(QString themepath, QString colorpath, QString font, QString fontsize);
+
+ //Additional info for a cursor theme
+ static QStringList cursorInformation(QString name); //returns: [Name, Comment, Sample Image File]
+
+ //Environment settings
+ static QStringList CustomEnvSettings(); //view all the key=value settings
+ static void LoadCustomEnvSettings(); //will push the custom settings into the environment (recommended before loading the initial QApplication)
+ static bool setCustomEnvSetting(QString var, QString val); //variable/value pair (use an empty val to clear it)
+ static QString readCustomEnvSetting(QString var);
+
+};
+
+// Qt Style override to allow custom themeing/colors
+/*class LuminaThemeStyle : public QProxyStyle{
+ Q_OBJECT
+private:
+ bool darkfont;
+
+public:
+ LuminaThemeStyle();
+ ~LuminaThemeStyle();
+
+ //Function to update the style (for use by the theme engine)
+ void update();
+ //Subclassed functions
+ void drawItemText(QPainter*, const QRect&, int, const QPalette&, bool, const QString&, QPalette::ColorRole) const;
+
+};*/
+
+//Simple class to setup a utility to use the Lumina theme
+//-----Example usage in "main.cpp" -------------------------------
+// LTHEME::LoadCustomEnvSettings();
+// QApplication a(argc,argv);
+// LuminaThemeEngine themes(&a)
+//------------------------------------------------------------------------------------
+// Note: If you also use LuminaXDG::findIcons() in the application and you want
+// to dynamically update those icons - connect to the updateIcons() signal
+//-------------------------------------------------------------------------------------
+// QMainWindow w; //(or whatever the main app window is)
+// QObject::connect(themes,SIGNAL(updateIcons()), &w, SLOT(updateIcons()) );
+//------------------------------------------------------------------------------------
+class LuminaThemeEngine : public QObject{
+ Q_OBJECT
+public:
+ LuminaThemeEngine(QApplication *app);
+ ~LuminaThemeEngine();
+
+ void refresh();
+
+private:
+ QApplication *application;
+ QFileSystemWatcher *watcher;
+ QString theme,colors,icons, font, fontsize, cursors; //current settings
+ QTimer *syncTimer;
+ QDateTime lastcheck;
+ //LuminaThemeStyle *style;
+
+private slots:
+ void watcherChange(QString);
+ void reloadFiles();
+
+signals:
+ void updateIcons(); //Icon theme changed
+ void updateCursors(); //Cursor theme changed
+ void EnvChanged(); //Some environment variable(s) changed
+};
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaUtils.cpp b/src-qt5/core/libLumina/LuminaUtils.cpp
new file mode 100644
index 00000000..e25596f8
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaUtils.cpp
@@ -0,0 +1,877 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2013-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LuminaUtils.h"
+
+#include <QString>
+#include <QFile>
+#include <QStringList>
+#include <QObject>
+#include <QTextCodec>
+#include <QDebug>
+#include <QDesktopWidget>
+#include <QImageReader>
+#include <QRegExp>
+#include <QFuture>
+#include <QtConcurrent>
+
+#include <LuminaOS.h>
+#include <LuminaThemes.h>
+#include <LuminaXDG.h>
+
+static QStringList fav;
+
+inline QStringList ProcessRun(QString cmd, QStringList args){
+ //Assemble outputs
+ QStringList out; out << "1" << ""; //error code, string output
+ QProcess proc;
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("LANG", "C");
+ env.insert("LC_MESSAGES", "C");
+ proc.setProcessEnvironment(env);
+ proc.setProcessChannelMode(QProcess::MergedChannels);
+ if(args.isEmpty()){
+ proc.start(cmd);
+ }else{
+ proc.start(cmd,args);
+ }
+ while(!proc.waitForFinished(500)){
+ if(proc.state() == QProcess::NotRunning){ break; } //somehow missed the finished signal
+ }
+ out[0] = QString::number(proc.exitCode());
+ out[1] = QString(proc.readAllStandardOutput());
+ return out;
+}
+//=============
+// LUtils Functions
+//=============
+QString LUtils::LuminaDesktopVersion(){
+ QString ver = "0.9.0-devel";
+ #ifdef GIT_VERSION
+ ver.append( QString(" (Git Revision: %1)").arg(GIT_VERSION) );
+ #endif
+ return ver;
+}
+
+QString LUtils::LuminaDesktopBuildDate(){
+ #ifdef BUILD_DATE
+ return BUILD_DATE;
+ #endif
+ return "";
+}
+
+int LUtils::runCmd(QString cmd, QStringList args){
+ /*QProcess proc;
+ proc.setProcessChannelMode(QProcess::MergedChannels);
+ if(args.isEmpty()){
+ proc.start(cmd);
+ }else{
+ proc.start(cmd, args);
+ }
+ //if(!proc.waitForStarted(30000)){ return 1; } //process never started - max wait of 30 seconds
+ while(!proc.waitForFinished(300)){
+ if(proc.state() == QProcess::NotRunning){ break; } //somehow missed the finished signal
+ QCoreApplication::processEvents();
+ }
+ int ret = proc.exitCode();
+ return ret;*/
+ QFuture<QStringList> future = QtConcurrent::run(ProcessRun, cmd, args);
+ return future.result()[0].toInt(); //turn it back into an integer return code
+
+}
+
+QStringList LUtils::getCmdOutput(QString cmd, QStringList args){
+ /*QProcess proc;
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("LANG", "C");
+ env.insert("LC_MESSAGES", "C");
+ proc.setProcessEnvironment(env);
+ proc.setProcessChannelMode(QProcess::MergedChannels);
+ if(args.isEmpty()){
+ proc.start(cmd);
+ }else{
+ proc.start(cmd,args);
+ }
+ //if(!proc.waitForStarted(30000)){ return QStringList(); } //process never started - max wait of 30 seconds
+ while(!proc.waitForFinished(300)){
+ if(proc.state() == QProcess::NotRunning){ break; } //somehow missed the finished signal
+ QCoreApplication::processEvents();
+ }
+ QStringList out = QString(proc.readAllStandardOutput()).split("\n");
+ return out;*/
+ QFuture<QStringList> future = QtConcurrent::run(ProcessRun, cmd, args);
+ return future.result()[1].split("\n"); //Split the return message into lines
+}
+
+QStringList LUtils::readFile(QString filepath){
+ QStringList out;
+ QFile file(filepath);
+ if(file.open(QIODevice::Text | QIODevice::ReadOnly)){
+ QTextStream in(&file);
+ while(!in.atEnd()){
+ out << in.readLine();
+ }
+ file.close();
+ }
+ return out;
+}
+
+bool LUtils::writeFile(QString filepath, QStringList contents, bool overwrite){
+ QFile file(filepath);
+ if(file.exists() && !overwrite){ return false; }
+ bool ok = false;
+ if(contents.isEmpty()){ contents << "\n"; }
+ if( file.open(QIODevice::WriteOnly | QIODevice::Truncate) ){
+ QTextStream out(&file);
+ out << contents.join("\n");
+ if(!contents.last().isEmpty()){ out << "\n"; } //always end with a new line
+ file.close();
+ ok = true;
+ }
+ return ok;
+}
+
+bool LUtils::isValidBinary(QString& bin){
+ if(!bin.startsWith("/")){
+ //Relative path: search for it on the current "PATH" settings
+ QStringList paths = QString(qgetenv("PATH")).split(":");
+ for(int i=0; i<paths.length(); i++){
+ if(QFile::exists(paths[i]+"/"+bin)){ bin = paths[i]+"/"+bin; break;}
+ }
+ }
+ //bin should be the full path by now
+ if(!bin.startsWith("/")){ return false; }
+ QFileInfo info(bin);
+ bool good = (info.exists() && info.isExecutable());
+ if(good){ bin = info.absoluteFilePath(); }
+ return good;
+}
+
+QString LUtils::GenerateOpenTerminalExec(QString term, QString dirpath){
+ //Check the input terminal application (default/fallback - determined by calling application)
+ //if(!LUtils::isValidBinary(term)){
+ if(term.endsWith(".desktop")){
+ //Pull the binary name out of the shortcut
+ bool ok = false;
+ XDGDesktop DF = LXDG::loadDesktopFile(term,ok);
+ if(!ok){ term = "xterm"; }
+ else{ term= DF.exec.section(" ",0,0); } //only take the binary name - not any other flags
+ }else{
+ term = "xterm"; //fallback
+ }
+ //}
+ //Now create the calling command for the designated terminal
+ // NOTE: While the "-e" routine is supposed to be universal, many terminals do not properly use it
+ // so add some special/known terminals here as necessary
+ QString exec;
+ qWarning() << " - Reached terminal initialization" << term;
+ if(term=="mate-terminal" || term=="lxterminal" || term=="gnome-terminal"){
+ exec = term+" --working-directory=\""+dirpath+"\"";
+ }else if(term=="xfce4-terminal"){
+ exec = term+" --default-working-directory=\""+dirpath+"\"";
+ }else if(term=="konsole"){
+ exec = term+" --workdir \""+dirpath+"\"";
+ }else{
+ //-e is the parameter for most of the terminal appliction to execute an external command.
+ //In this case we start a shell in the selected directory
+ //Need the user's shell first
+ QString shell = QString(getenv("SHELL"));
+ if(!LUtils::isValidBinary(shell)){ shell = "/bin/sh"; } //universal fallback for a shell
+ exec = term + " -e \"cd " + dirpath + " && " + shell + " \" ";
+ }
+ qDebug() << exec;
+ return exec;
+}
+
+QStringList LUtils::listSubDirectories(QString dir, bool recursive){
+ //This is a recursive method for returning the full paths of all subdirectories (if recursive flag is enabled)
+ QDir maindir(dir);
+ QStringList out;
+ QStringList subs = maindir.entryList(QDir::NoDotAndDotDot | QDir::Dirs, QDir::Name);
+ for(int i=0; i<subs.length(); i++){
+ out << maindir.absoluteFilePath(subs[i]);
+ if(recursive){
+ out << LUtils::listSubDirectories(maindir.absoluteFilePath(subs[i]), recursive);
+ }
+ }
+ return out;
+}
+
+QString LUtils::PathToAbsolute(QString path){
+ //Convert an input path to an absolute path (this does not check existance ot anything)
+ if(path.startsWith("/")){ return path; } //already an absolute path
+ if(path.startsWith("~")){ path.replace(0,1,QDir::homePath()); }
+ if(!path.startsWith("/")){
+ //Must be a relative path
+ if(path.startsWith("./")){ path = path.remove(2); }
+ path.prepend( QDir::currentPath()+"/");
+ }
+ return path;
+}
+
+QStringList LUtils::imageExtensions(bool wildcards){
+ //Note that all the image extensions are lowercase!!
+ static QStringList imgExtensions;
+ if(imgExtensions.isEmpty()){
+ QList<QByteArray> fmt = QImageReader::supportedImageFormats();
+ for(int i=0; i<fmt.length(); i++){
+ if(wildcards){ imgExtensions << "*."+QString::fromLocal8Bit(fmt[i]); }
+ else{ imgExtensions << QString::fromLocal8Bit(fmt[i]); }
+ }
+ }
+ return imgExtensions;
+}
+
+ QTranslator* LUtils::LoadTranslation(QApplication *app, QString appname, QString locale, QTranslator *cTrans){
+ //Get the current localization
+ QString langEnc = "UTF-8"; //default value
+ QString langCode = locale; //provided locale
+ if(langCode.isEmpty()){ langCode = getenv("LC_ALL"); }
+ if(langCode.isEmpty()){ langCode = getenv("LANG"); }
+ if(langCode.isEmpty()){ langCode = "en_US.UTF-8"; } //default to US english
+ //See if the encoding is included and strip it out as necessary
+ if(langCode.contains(".")){
+ langEnc = langCode.section(".",-1);
+ langCode = langCode.section(".",0,0);
+ }
+ //Now verify the encoding for the locale
+ if(langCode =="C" || langCode=="POSIX" || langCode.isEmpty()){
+ langEnc = "System"; //use the Qt system encoding
+ }
+ if(app !=0){
+ qDebug() << "Loading Locale:" << appname << langCode << langEnc;
+ //If an existing translator was provided, remove it first (will be replaced)
+ if(cTrans!=0){ app->removeTranslator(cTrans); }
+ //Setup the translator
+ cTrans = new QTranslator();
+ //Use the shortened locale code if specific code does not have a corresponding file
+ if(!QFile::exists(LOS::LuminaShare()+"i18n/"+appname+"_" + langCode + ".qm") && langCode!="en_US" ){
+ langCode.truncate( langCode.indexOf("_") );
+ }
+ if( cTrans->load( appname+QString("_") + langCode, LOS::LuminaShare()+"i18n/" ) ){
+ app->installTranslator( cTrans );
+ }else{
+ //Translator could not be loaded for some reason
+ cTrans = 0;
+ if(langCode!="en_US"){
+ qWarning() << " - Could not load Locale:" << langCode;
+ }
+ }
+ }else{
+ //Only going to set the encoding since no application given
+ qDebug() << "Loading System Encoding:" << langEnc;
+ }
+ //Load current encoding for this locale
+ QTextCodec::setCodecForLocale( QTextCodec::codecForName(langEnc.toUtf8()) );
+ return cTrans;
+}
+
+QStringList LUtils::knownLocales(){
+ QDir i18n = QDir(LOS::LuminaShare()+"i18n");
+ if( !i18n.exists() ){ return QStringList(); }
+ QStringList files = i18n.entryList(QStringList() << "lumina-desktop_*.qm", QDir::Files, QDir::Name);
+ if(files.isEmpty()){ return QStringList(); }
+ //Now strip off the filename and just leave the locale tag
+ for(int i=0; i<files.length(); i++){
+ files[i].chop(3); //remove the ".qm" on the end
+ files[i] = files[i].section("_",1,50).simplified();
+ }
+ files << "en_US"; //default locale
+ files.sort();
+ return files;
+}
+
+void LUtils::setLocaleEnv(QString lang, QString msg, QString time, QString num,QString money,QString collate, QString ctype){
+ //Adjust the current locale environment variables
+ bool all = false;
+ if(msg.isEmpty() && time.isEmpty() && num.isEmpty() && money.isEmpty() && collate.isEmpty() && ctype.isEmpty() ){
+ if(lang.isEmpty()){ return; } //nothing to do - no changes requested
+ all = true; //set everything to the "lang" value
+ }
+ //If no lang given, but others are given, then use the current setting
+ if(lang.isEmpty()){ lang = getenv("LC_ALL"); }
+ if(lang.isEmpty()){ lang = getenv("LANG"); }
+ if(lang.isEmpty()){ lang = "en_US"; }
+ //Now go through and set/unset the environment variables
+ // - LANG & LC_ALL
+ if(!lang.contains(".")){ lang.append(".UTF-8"); }
+ setenv("LANG",lang.toUtf8() ,1); //overwrite setting (this is always required as the fallback)
+ if(all){ setenv("LC_ALL",lang.toUtf8() ,1); }
+ else{ unsetenv("LC_ALL"); } //make sure the custom settings are used
+ // - LC_MESSAGES
+ if(msg.isEmpty()){ unsetenv("LC_MESSAGES"); }
+ else{
+ if(!msg.contains(".")){ msg.append(".UTF-8"); }
+ setenv("LC_MESSAGES",msg.toUtf8(),1);
+ }
+ // - LC_TIME
+ if(time.isEmpty()){ unsetenv("LC_TIME"); }
+ else{
+ if(!time.contains(".")){ time.append(".UTF-8"); }
+ setenv("LC_TIME",time.toUtf8(),1);
+ }
+ // - LC_NUMERIC
+ if(num.isEmpty()){ unsetenv("LC_NUMERIC"); }
+ else{
+ if(!num.contains(".")){ num.append(".UTF-8"); }
+ setenv("LC_NUMERIC",num.toUtf8(),1);
+ }
+ // - LC_MONETARY
+ if(money.isEmpty()){ unsetenv("LC_MONETARY"); }
+ else{
+ if(!money.contains(".")){ money.append(".UTF-8"); }
+ setenv("LC_MONETARY",money.toUtf8(),1);
+ }
+ // - LC_COLLATE
+ if(collate.isEmpty()){ unsetenv("LC_COLLATE"); }
+ else{
+ if(!collate.contains(".")){ collate.append(".UTF-8"); }
+ setenv("LC_COLLATE",collate.toUtf8(),1);
+ }
+ // - LC_CTYPE
+ if(ctype.isEmpty()){ unsetenv("LC_CTYPE"); }
+ else{
+ if(!ctype.contains(".")){ ctype.append(".UTF-8"); }
+ setenv("LC_CTYPE",ctype.toUtf8(),1);
+ }
+}
+
+QString LUtils::currentLocale(){
+ QString curr = getenv("LC_ALL");// = QLocale::system();
+ if(curr.isEmpty()){ curr = getenv("LANG"); }
+ if(curr.isEmpty()){ curr = "en_US"; }
+ curr = curr.section(".",0,0); //remove any encodings off the end
+ return curr;
+}
+
+double LUtils::DisplaySizeToBytes(QString num){
+ //qDebug() << "Convert Num to Bytes:" << num;
+ num = num.toLower().simplified();
+ num = num.remove(" ");
+ if(num.isEmpty()){ return 0.0; }
+ if(num.endsWith("b")){ num.chop(1); } //remove the "bytes" marker (if there is one)
+ QString lab = "b";
+ if(!num[num.size()-1].isNumber()){
+ lab = num.right(1); num.chop(1);
+ }
+ double N = num.toDouble();
+ QStringList labs; labs <<"b"<<"k"<<"m"<<"g"<<"t"<<"p"; //go up to petabytes for now
+ for(int i=0; i<labs.length(); i++){
+ if(lab==labs[i]){ break; }//already at the right units - break out
+ N = N*1024.0; //Move to the next unit of measurement
+ }
+ //qDebug() << " - Done:" << QString::number(N) << lab << num;
+ return N;
+}
+
+QString LUtils::BytesToDisplaySize(qint64 ibytes){
+ static QStringList labs = QStringList();
+ if(labs.isEmpty()){ labs << "B" << "K" << "M" << "G" << "T" << "P"; }
+ //Now get the dominant unit
+ int c=0;
+ double bytes = ibytes; //need to keep decimel places for calculations
+ while(bytes>=1000 && c<labs.length() ){
+ bytes = bytes/1024;
+ c++;
+ } //labs[c] is the unit
+ //Bytes are now
+ //Now format the number (up to 3 digits, not including decimel places)
+ QString num;
+ if(bytes>=100){
+ //No decimel places
+ num = QString::number(qRound(bytes));
+ }else if(bytes>=10){
+ //need 1 decimel place
+ num = QString::number( (qRound(bytes*10)/10.0) );
+ }else if(bytes>1){
+ //need 2 decimel places
+ num = QString::number( (qRound(bytes*100)/100.0) );
+ }else{
+ //Fully decimel (3 places)
+ num = "0."+QString::number(qRound(bytes*1000));
+ }
+ return (num+labs[c]);
+}
+
+QString LUtils::SecondsToDisplay(int secs){
+ if(secs < 0){ return "??"; }
+ QString rem; //remaining
+ if(secs > 3600){
+ int hours = secs/3600;
+ rem.append( QString::number(hours)+"h ");
+ secs = secs - (hours*3600);
+ }
+ if(secs > 60){
+ int min = secs/60;
+ rem.append( QString::number(min)+"m ");
+ secs = secs - (min*60);
+ }
+ if(secs > 0){
+ rem.append( QString::number(secs)+"s");
+ }else{
+ rem.append( "0s" );
+ }
+ return rem;
+}
+
+//Various function for finding valid QtQuick plugins on the system
+bool LUtils::validQuickPlugin(QString ID){
+ return ( !LUtils::findQuickPluginFile(ID).isEmpty() );
+}
+
+QString LUtils::findQuickPluginFile(QString ID){
+ if(ID.startsWith("quick-")){ ID = ID.section("-",1,50); } //just in case
+ //Give preference to any user-supplied plugins (overwrites for system plugins)
+ QString path = QDir::homePath()+"/.lumina/quickplugins/quick-"+ID+".qml";
+ if( QFile::exists(path) ){return path; }
+ path = LOS::LuminaShare()+"quickplugins/quick-"+ID+".qml";
+ if( QFile::exists(path) ){return path; }
+ return ""; //could not be found
+}
+
+QStringList LUtils::listQuickPlugins(){
+ QDir dir(QDir::homePath()+"/.lumina/quickplugins");
+ QStringList files = dir.entryList(QStringList() << "quick-*.qml", QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
+ dir.cd(LOS::LuminaShare()+"quickplugins");
+ files << dir.entryList(QStringList() << "quick-*.qml", QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
+ for(int i=0; i<files.length(); i++){
+ files[i] = files[i].section("quick-",1,100).section(".qml",0,0); //just grab the ID out of the middle of the filename
+ }
+ files.removeDuplicates();
+ //qDebug() << "Found Quick Plugins:" << files;
+ return files;
+}
+
+QStringList LUtils::infoQuickPlugin(QString ID){ //Returns: [Name, Description, Icon]
+ //qDebug() << "Find Quick Info:" << ID;
+ QString path = findQuickPluginFile(ID);
+ //qDebug() << " - path:" << path;
+ if(path.isEmpty()){ return QStringList(); } //invalid ID
+ QStringList contents = LUtils::readFile(path);
+ if(contents.isEmpty()){ return QStringList(); } //invalid file (unreadable)
+ contents = contents.filter("//").filter("=").filter("Plugin"); //now just grab the comments
+ //qDebug() << " - Filtered Contents:" << contents;
+ QStringList info; info << "" << "" << "";
+ for(int i=0; i<contents.length(); i++){
+ if(contents[i].contains("Plugin-Name=")){ info[0] = contents[i].section("Plugin-Name=",1,1).simplified(); }
+ else if(contents[i].contains("Plugin-Description=")){ info[1] = contents[i].section("Plugin-Description=",1,1).simplified(); }
+ else if(contents[i].contains("Plugin-Icon=")){ info[2] = contents[i].section("Plugin-Icon=",1,1).simplified(); }
+ }
+ if(info[0].isEmpty()){ info[0]=ID; }
+ if(info[2].isEmpty()){ info[2]="preferences-plugin"; }
+ //qDebug() << " - info:" << info;
+ return info;
+}
+
+QStringList LUtils::listFavorites(){
+ static QDateTime lastRead;
+ QDateTime cur = QDateTime::currentDateTime();
+ if(lastRead.isNull() || lastRead<QFileInfo(QDir::homePath()+"/.lumina/favorites/fav.list").lastModified()){
+ fav = LUtils::readFile(QDir::homePath()+"/.lumina/favorites/fav.list");
+ fav.removeAll(""); //remove any empty lines
+ lastRead = cur;
+ if(fav.isEmpty()){
+ //Make sure the favorites dir exists, and create it if necessary
+ QDir dir(QDir::homePath()+"/.lumina/favorites");
+ if(!dir.exists()){ dir.mkpath(QDir::homePath()+"/.lumina/favorites"); }
+ }
+ }
+
+ return fav;
+}
+
+bool LUtils::saveFavorites(QStringList list){
+ bool ok = LUtils::writeFile(QDir::homePath()+"/.lumina/favorites/fav.list", list, true);
+ if(ok){ fav = list; } //also save internally in case of rapid write/read of the file
+ return ok;
+}
+
+bool LUtils::isFavorite(QString path){
+ QStringList fav = LUtils::listFavorites();
+ for(int i=0; i<fav.length(); i++){
+ if(fav[i].endsWith("::::"+path)){ return true; }
+ }
+ return false;
+}
+
+bool LUtils::addFavorite(QString path, QString name){
+ //Generate the type of favorite this is
+ QFileInfo info(path);
+ QString type;
+ if(info.isDir()){ type="dir"; }
+ else if(info.suffix()=="desktop"){ type="app"; }
+ else{ type = LXDG::findAppMimeForFile(path); }
+ //Assign a name if none given
+ if(name.isEmpty()){ name = info.fileName(); }
+ //Now add it to the list
+ QStringList favs = LUtils::listFavorites();
+ bool found = false;
+ for(int i=0; i<favs.length(); i++){
+ if(favs[i].endsWith("::::"+path)){ favs[i] = name+"::::"+type+"::::"+path; }
+ }
+ if(!found){ favs << name+"::::"+type+"::::"+path; }
+ return LUtils::saveFavorites(favs);
+}
+
+void LUtils::removeFavorite(QString path){
+ QStringList fav = LUtils::listFavorites();
+ bool changed = false;
+ for(int i=0; i<fav.length(); i++){
+ if(fav[i].endsWith("::::"+path)){ fav.removeAt(i); i--; changed=true;}
+ }
+ if(changed){ LUtils::saveFavorites(fav); }
+}
+
+void LUtils::upgradeFavorites(int fromoldversionnumber){
+ if(fromoldversionnumber <= 8004){ // < pre-0.8.4>, sym-links in the ~/.lumina/favorites dir}
+ //Include 0.8.4-devel versions in this upgrade (need to distinguish b/w devel and release versions later somehow)
+ QDir favdir(QDir::homePath()+"/.lumina/favorites");
+ QFileInfoList symlinks = favdir.entryInfoList(QDir::Files | QDir::Dirs | QDir::System | QDir::NoDotAndDotDot);
+ QStringList favfile = LUtils::listFavorites(); //just in case some already exist
+ bool newentry = false;
+ for(int i=0; i<symlinks.length(); i++){
+ if(!symlinks[i].isSymLink()){ continue; } //not a symlink
+ QString path = symlinks[i].symLinkTarget();
+ QString name = symlinks[i].fileName(); //just use the name of the symlink from the old system
+ QString type;
+ if(symlinks[i].isDir()){ type = "dir"; }
+ else if(name.endsWith(".desktop")){ type = "app"; }
+ else{ type = LXDG::findAppMimeForFile(path); }
+ //Put the line into the file
+ favfile << name+"::::"+type+"::::"+path;
+ //Now remove the symlink - obsolete format
+ QFile::remove(symlinks[i].absoluteFilePath());
+ newentry = true;
+ }
+ if(newentry){
+ LUtils::saveFavorites(favfile);
+ }
+ } //end check for version <= 0.8.4
+
+}
+
+void LUtils::LoadSystemDefaults(bool skipOS){
+ //Will create the Lumina configuration files based on the current system template (if any)
+ qDebug() << "Loading System Defaults";
+ QStringList sysDefaults;
+ if(!skipOS){ sysDefaults = LUtils::readFile(LOS::AppPrefix()+"etc/luminaDesktop.conf"); }
+ if(sysDefaults.isEmpty() && !skipOS){ sysDefaults = LUtils::readFile(LOS::AppPrefix()+"etc/luminaDesktop.conf.dist"); }
+ if(sysDefaults.isEmpty() && !skipOS) { sysDefaults = LUtils::readFile(LOS::SysPrefix()+"etc/luminaDesktop.conf"); }
+ if(sysDefaults.isEmpty() && !skipOS){ sysDefaults = LUtils::readFile(LOS::SysPrefix()+"etc/luminaDesktop.conf.dist"); }
+ if(sysDefaults.isEmpty() && !skipOS) { sysDefaults = LUtils::readFile(L_ETCDIR+"/luminaDesktop.conf"); }
+ if(sysDefaults.isEmpty() && !skipOS){ sysDefaults = LUtils::readFile(L_ETCDIR+"/luminaDesktop.conf.dist"); }
+ if(sysDefaults.isEmpty()){ sysDefaults = LUtils::readFile(LOS::LuminaShare()+"luminaDesktop.conf"); }
+ //Find the number of the left-most desktop screen
+ QString screen = "0";
+ QDesktopWidget *desk =QApplication::desktop();
+ QRect screenGeom;
+ for(int i=0; i<desk->screenCount(); i++){
+ if(desk->screenGeometry(i).x()==0){
+ screen = QString::number(i);
+ screenGeom = desk->screenGeometry(i);
+ break;
+ }
+ }
+ //Now setup the default "desktopsettings.conf" and "sessionsettings.conf" files
+ QStringList deskset, sesset, lopenset;
+
+ // -- SESSION SETTINGS --
+ QStringList tmp = sysDefaults.filter("session_");
+ if(tmp.isEmpty()){ tmp = sysDefaults.filter("session."); }//for backwards compat
+ sesset << "[General]"; //everything is in this section
+ sesset << "DesktopVersion="+LUtils::LuminaDesktopVersion();
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ if(val.isEmpty()){ continue; }
+ QString istrue = (val.toLower()=="true") ? "true": "false";
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+
+ //Special handling for values which need to exist first
+ if(var.endsWith("_ifexists") ){
+ var = var.remove("_ifexists"); //remove this flag from the variable
+ //Check if the value exists (absolute path only)
+ if(!QFile::exists(val)){ continue; } //skip this line - value/file does not exist
+ }
+ //Parse/save the value
+ QString loset, sset; //temporary strings
+ if(var=="session_enablenumlock"){ sset = "EnableNumlock="+ istrue; }
+ else if(var=="session_playloginaudio"){ sset = "PlayStartupAudio="+istrue; }
+ else if(var=="session_playlogoutaudio"){ sset = "PlayLogoutAudio="+istrue; }
+ else if(var=="session_default_terminal"){ sset = "default-terminal="+val; }
+ else if(var=="session_default_filemanager"){
+ sset = "default-filemanager="+val;
+ loset = "directory="+val;
+ }
+ else if(var=="session_default_webbrowser"){ loset = "webbrowser="+val; }
+ else if(var=="session_default_email"){ loset = "email="+val; }
+ //Put the line into the file (overwriting any previous assignment as necessary)
+ if(!loset.isEmpty()){
+ int index = lopenset.indexOf(QRegExp(loset.section("=",0,0)+"=*", Qt::CaseSensitive, QRegExp::Wildcard));
+ qDebug() << "loset line:" << loset << index << lopenset;
+ if(index<0){ lopenset << loset; } //new line
+ else{ lopenset[index] = loset; } //overwrite the other line
+ }
+ if(!sset.isEmpty()){
+ int index = sesset.indexOf(QRegExp(sset.section("=",0,0)+"=*", Qt::CaseSensitive, QRegExp::Wildcard));
+ if(index<0){ sesset << sset; } //new line
+ else{ sesset[index] = sset; } //overwrite the other line
+ }
+ }
+ if(!lopenset.isEmpty()){ lopenset.prepend("[default]"); } //the session options exist within this set
+
+ // -- DESKTOP SETTINGS --
+ //(only works for the primary desktop at the moment)
+ tmp = sysDefaults.filter("desktop_");
+ if(tmp.isEmpty()){ tmp = sysDefaults.filter("desktop."); }//for backwards compat
+ if(!tmp.isEmpty()){deskset << "[desktop-"+screen+"]"; }
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ if(val.isEmpty()){ continue; }
+ QString istrue = (val.toLower()=="true") ? "true": "false";
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+ if(var=="desktop_visiblepanels"){ deskset << "panels="+val; }
+ else if(var=="desktop_backgroundfiles"){ deskset << "background\\filelist="+val; }
+ else if(var=="desktop_backgroundrotateminutes"){ deskset << "background\\minutesToChange="+val; }
+ else if(var=="desktop_plugins"){ deskset << "pluginlist="+val; }
+ else if(var=="desktop_generate_icons"){ deskset << "generateDesktopIcons="+istrue; }
+ }
+ if(!tmp.isEmpty()){ deskset << ""; } //space between sections
+
+ // -- PANEL SETTINGS --
+ //(only works for the primary desktop at the moment)
+ for(int i=1; i<11; i++){
+ QString panvar = "panel"+QString::number(i);
+ tmp = sysDefaults.filter(panvar);
+ if(!tmp.isEmpty()){deskset << "[panel"+screen+"."+QString::number(i-1)+"]"; }
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ if(val.isEmpty()){ continue; }
+ QString istrue = (val.toLower()=="true") ? "true": "false";
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+ if(var==(panvar+"_pixelsize")){
+ //qDebug() << "Panel Size:" << val;
+ if(val.contains("%")){
+ QString last = val.section("%",1,1).toLower(); //last character
+ val = val.section("%",0,0);
+ if(last=="h"){ val = QString::number( qRound(screenGeom.height()*val.toDouble())/100 ); }//adjust value to a percentage of the height of the screen
+ else if(last=="w"){ val = QString::number( qRound(screenGeom.width()*val.toDouble())/100 ); }//adjust value to a percentage of the width of the screen
+ }
+ //qDebug() << " -- Adjusted:" << val;
+ deskset << "height="+val;
+ }
+ else if(var==(panvar+"_autohide")){ deskset << "hidepanel="+istrue; }
+ else if(var==(panvar+"_location")){ deskset << "location="+val.toLower(); }
+ else if(var==(panvar+"_plugins")){ deskset << "pluginlist="+val; }
+ else if(var==(panvar+"_pinlocation")){ deskset << "pinLocation="+val.toLower(); }
+ else if(var==(panvar+"_edgepercent")){ deskset << "lengthPercent="+val; }
+ }
+ if(!tmp.isEmpty()){ deskset << ""; } //space between sections
+ }
+
+ // -- MENU settings --
+ tmp = sysDefaults.filter("menu_");
+ if(tmp.isEmpty()){ tmp = sysDefaults.filter("menu."); } //backwards compat
+ if(!tmp.isEmpty()){deskset << "[menu]"; }
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).toLower().simplified();
+ if(val.isEmpty()){ continue; }
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+ if(var=="menu_plugins"){ deskset << "itemlist="+val; }
+ }
+ if(!tmp.isEmpty()){ deskset << ""; } //space between sections
+
+ // -- FAVORITES --
+ tmp = sysDefaults.filter("favorites_");
+ if(tmp.isEmpty()){ tmp = sysDefaults.filter("favorites."); }
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+ qDebug() << "Favorite entry:" << var << val;
+ if(var=="favorites_add_ifexists" && QFile::exists(val)){ qDebug() << " - Exists/Adding:"; LUtils::addFavorite(val); }
+ else if(var=="favorites_add"){ qDebug() << " - Adding:"; LUtils::addFavorite(val); }
+ else if(var=="favorites_remove"){ qDebug() << " - Removing:"; LUtils::removeFavorite(val); }
+ }
+
+ //Now do any theme settings
+ QStringList themesettings = LTHEME::currentSettings();
+ //List: [theme path, colorspath, iconsname, font, fontsize]
+ //qDebug() << "Current Theme Color:" << themesettings[1];
+ tmp = sysDefaults.filter("theme_");
+ if(tmp.isEmpty()){ tmp = sysDefaults.filter("theme."); }
+ bool setTheme = !tmp.isEmpty();
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ if(val.isEmpty()){ continue; }
+ //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while
+ if(var.contains(".")){ var.replace(".","_"); }
+ //Now parse the variable and put the value in the proper file
+ if(var=="theme_themefile"){ themesettings[0] = val; }
+ else if(var=="theme_colorfile"){ themesettings[1] = val; }
+ else if(var=="theme_iconset"){ themesettings[2] = val; }
+ else if(var=="theme_font"){ themesettings[3] = val; }
+ else if(var=="theme_fontsize"){
+ if(val.endsWith("%")){ val = QString::number( (screenGeom.height()*val.section("%",0,0).toDouble())/100 )+"px"; }
+ themesettings[4] = val;
+ }
+ }
+ //qDebug() << " - Now Color:" << themesettings[1] << setTheme;
+
+ //Now double check that the custom theme/color files exist and reset it will the full path as necessary
+ if(setTheme){
+ QStringList systhemes = LTHEME::availableSystemThemes();
+ QStringList syscolors = LTHEME::availableSystemColors();
+ //theme file
+ //qDebug() << "Detected Themes/colors:" << systhemes << syscolors;
+ if( !themesettings[0].startsWith("/") || !QFile::exists(themesettings[0]) || !themesettings[0].endsWith(".qss.template")){
+ themesettings[0] = themesettings[0].section(".qss",0,0).simplified();
+ for(int i=0; i<systhemes.length(); i++){
+ if(systhemes[i].startsWith(themesettings[0]+"::::",Qt::CaseInsensitive)){
+ themesettings[0] = systhemes[i].section("::::",1,1); //Replace with the full path
+ break;
+ }
+ }
+ }
+ //color file
+ if( !themesettings[1].startsWith("/") || !QFile::exists(themesettings[1]) || !themesettings[1].endsWith(".qss.colors") ){
+ //Remove any extra/invalid extension
+ themesettings[1] = themesettings[1].section(".qss",0,0).simplified();
+ for(int i=0; i<syscolors.length(); i++){
+ if(syscolors[i].startsWith(themesettings[1]+"::::",Qt::CaseInsensitive)){
+ themesettings[1] = syscolors[i].section("::::",1,1); //Replace with the full path
+ break;
+ }
+ }
+ }
+ }
+ //qDebug() << " - Final Theme Color:" << themesettings[1];
+
+ //Ensure that the settings directory exists
+ QString setdir = QDir::homePath()+"/.lumina/LuminaDE";
+ if(!QFile::exists(setdir)){
+ QDir dir;
+ dir.mkpath(setdir);
+ }
+ //Now save the settings files
+ if(setTheme){ LTHEME::setCurrentSettings( themesettings[0], themesettings[1], themesettings[2], themesettings[3], themesettings[4]); }
+ LUtils::writeFile(setdir+"/sessionsettings.conf", sesset, true);
+ LUtils::writeFile(setdir+"/desktopsettings.conf", deskset, true);
+ LUtils::writeFile(setdir+"/lumina-open.conf", lopenset, true);
+}
+
+// =======================
+// RESIZEMENU CLASS
+// =======================
+ResizeMenu::ResizeMenu(QWidget *parent) : QMenu(parent){
+ this->setContentsMargins(1,1,1,1);
+ this->setMouseTracking(true);
+ resizeSide = NONE;
+ cAct = new QWidgetAction(this);
+ contents = 0;
+ connect(this, SIGNAL(aboutToShow()), this, SLOT(clearFlags()) );
+ connect(this, SIGNAL(aboutToHide()), this, SLOT(clearFlags()) );
+ connect(cAct, SIGNAL(hovered()), this, SLOT(actionHovered()) );
+}
+
+ResizeMenu::~ResizeMenu(){
+
+}
+
+void ResizeMenu::setContents(QWidget *con){
+ this->clear();
+ cAct->setDefaultWidget(con);
+ this->addAction(cAct);
+ contents = con; //save for later
+ contents->setCursor(Qt::ArrowCursor);
+}
+
+void ResizeMenu::mouseMoveEvent(QMouseEvent *ev){
+ QRect geom = this->geometry();
+ //Note: The exact position does not matter as much as the size
+ // since the window will be moved again the next time it is shown
+ // The "-2" in the sizing below accounts for the menu margins
+ QPoint gpos = this->mapToGlobal(ev->pos());
+ switch(resizeSide){
+ case TOP:
+ if(gpos.y() >= geom.bottom()-1){ break; }
+ geom.setTop(gpos.y());
+ this->setGeometry(geom);
+ if(contents!=0){ contents->setFixedSize(QSize(geom.width()-2, geom.height()-2)); }
+ break;
+ case BOTTOM:
+ if(gpos.y() <= geom.top()+1){ break; }
+ geom.setBottom( gpos.y());
+ this->setGeometry(geom);
+ if(contents!=0){ contents->setFixedSize(QSize(geom.width()-2, geom.height()-2)); }
+ break;
+ case LEFT:
+ if(gpos.x() >= geom.right()-1){ break; }
+ geom.setLeft(gpos.x());
+ this->setGeometry(geom);
+ if(contents!=0){ contents->setFixedSize(QSize(geom.width()-2, geom.height()-2)); }
+ break;
+ case RIGHT:
+ if(gpos.x() <= geom.left()+1){ break; }
+ geom.setRight(gpos.x());
+ this->setGeometry(geom);
+ if(contents!=0){ contents->setFixedSize(QSize(geom.width()-2, geom.height()-2)); }
+ break;
+ default: //NONE
+ //qDebug() << " - Mouse At:" << ev->pos();
+ //Just adjust the mouse cursor which is shown
+ if(ev->pos().x()<=1 && ev->pos().x() >= -1){ this->setCursor(Qt::SizeHorCursor); }
+ else if(ev->pos().x() >= this->width()-1 && ev->pos().x() <= this->width()+1){ this->setCursor(Qt::SizeHorCursor); }
+ else if(ev->pos().y()<=1 && ev->pos().y() >= -1){ this->setCursor(Qt::SizeVerCursor); }
+ else if(ev->pos().y() >= this->height()-1 && ev->pos().y() <= this->height()+1){ this->setCursor(Qt::SizeVerCursor); }
+ else{ this->setCursor(Qt::ArrowCursor); }
+ }
+ QMenu::mouseMoveEvent(ev); //do normal processing as well
+}
+
+void ResizeMenu::mousePressEvent(QMouseEvent *ev){
+ bool used = false;
+ if(ev->buttons().testFlag(Qt::LeftButton) && resizeSide==NONE){
+ //qDebug() << "Mouse Press Event:" << ev->pos() << resizeSide;
+ if(ev->pos().x()<=1 && ev->pos().x() >= -1){resizeSide = LEFT; used = true;}
+ else if(ev->pos().x() >= this->width()-1 && ev->pos().x() <= this->width()+1){ resizeSide = RIGHT; used = true;}
+ else if(ev->pos().y()<=1 && ev->pos().y() >= -1){ resizeSide = TOP; used = true; }
+ else if(ev->pos().y() >= this->height()-1 && ev->pos().y() <= this->height()+1){ resizeSide = BOTTOM; used = true; }
+ }
+ if(used){ ev->accept(); }
+ else{ QMenu::mousePressEvent(ev); } //do normal processing
+}
+
+void ResizeMenu::mouseReleaseEvent(QMouseEvent *ev){
+ if(ev->button() == Qt::LeftButton && resizeSide!=NONE ){
+ //qDebug() << "Mouse Release Event:" << ev->pos() << resizeSide;
+ resizeSide = NONE;
+ emit MenuResized(contents->size());
+ ev->accept();
+ }else{
+ QMenu::mouseReleaseEvent(ev); //do normal processing
+ }
+}
diff --git a/src-qt5/core/libLumina/LuminaUtils.h b/src-qt5/core/libLumina/LuminaUtils.h
new file mode 100644
index 00000000..f0e4b88b
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaUtils.h
@@ -0,0 +1,128 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2012-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#ifndef _LUMINA_LIBRARY_UTILS_H
+#define _LUMINA_LIBRARY_UTILS_H
+
+#include <QCoreApplication>
+#include <QProcess>
+#include <QTextStream>
+#include <QFile>
+#include <QDir>
+#include <QString>
+#include <QStringList>
+#include <QFile>
+#include <QFileInfo>
+#include <QObject>
+#include <QTranslator>
+//#include <QApplication>
+#include <QMenu>
+#include <QMouseEvent>
+#include <QSize>
+#include <QWidgetAction>
+
+class LUtils{
+public:
+ //Get the current version/build of the Lumina desktop
+ static QString LuminaDesktopVersion();
+ static QString LuminaDesktopBuildDate();
+
+ //Run an external command and return the exit code
+ static int runCmd(QString cmd, QStringList args = QStringList());
+ //Run an external command and return any text output (one line per entry)
+ static QStringList getCmdOutput(QString cmd, QStringList args = QStringList());
+
+ //Read a text file
+ static QStringList readFile(QString filepath);
+ //Write a text file
+ static bool writeFile(QString filepath, QStringList contents, bool overwrite=false);
+
+ //Check whether a file/path is a valid binary
+ static bool isValidBinary(QString& bin); //full path or name only
+ static bool isValidBinary(const char *bin){
+ QString bins(bin);
+ return isValidBinary(bins); //overload for a "junk" binary variable input
+ }
+
+ //Create the exec string to open a terminal in a particular directory
+ static QString GenerateOpenTerminalExec(QString term, QString dirpath);
+
+ //List all the sub-directories of a parent dir (recursive)
+ static QStringList listSubDirectories(QString dir, bool recursive = true);
+
+ //Convert an input file/dir path to an absolute file path
+ static QString PathToAbsolute(QString path);
+
+ //Get the list of all file extensions which Qt can read (lowercase)
+ static QStringList imageExtensions(bool wildcards = false);
+
+ //Load a translation file for a Lumina Project
+ static QTranslator* LoadTranslation(QApplication *app, QString appname, QString locale = "", QTranslator *cTrans = 0);
+ //Other localization shortcuts
+ static QStringList knownLocales(); //Note: This only lists locales known to Lumina (so the i18n files need to be installed)
+ static void setLocaleEnv(QString lang, QString msg="", QString time="", QString num="" ,QString money="",QString collate="", QString ctype="");
+ static QString currentLocale();
+
+ //Number format conversions
+ static double DisplaySizeToBytes(QString num); //Turn a display size (like 50M or 50KB) into a double for calculations (bytes)
+ static QString BytesToDisplaySize(qint64 bytes); //convert into a readable size (like 50M or 50KB)
+
+ static QString SecondsToDisplay(int secs); //convert into a readable time
+
+ //Various function for finding valid QtQuick plugins on the system
+ static bool validQuickPlugin(QString ID);
+ static QString findQuickPluginFile(QString ID);
+ static QStringList listQuickPlugins(); //List of valid ID's
+ static QStringList infoQuickPlugin(QString ID); //Returns: [Name, Description, Icon]
+
+ //Various functions for the favorites sub-system
+ // Formatting Note: "<name>::::[dir/app/<mimetype>]::::<path>"
+ // the <name> field might not be used for "app" flagged entries
+ static QStringList listFavorites();
+ static bool saveFavorites(QStringList);
+ static bool isFavorite(QString path);
+ static bool addFavorite(QString path, QString name = "");
+ static void removeFavorite(QString path);
+ static void upgradeFavorites(int fromoldversionnumber);
+
+ //Load the default setup for the system
+ static void LoadSystemDefaults(bool skipOS = false);
+
+};
+
+//Special subclass for a menu which the user can grab the edges and resize as necessary
+// Note: Make sure that you don't set 0pixel contents margins on this menu
+// - it needs at least 1 pixel margins for the user to be able to grab it
+class ResizeMenu : public QMenu{
+ Q_OBJECT
+public:
+ ResizeMenu(QWidget *parent = 0);
+ virtual ~ResizeMenu();
+
+ void setContents(QWidget *con);
+
+private:
+ enum SideFlag{NONE, TOP, BOTTOM, LEFT, RIGHT};
+ SideFlag resizeSide;
+ QWidget *contents;
+ QWidgetAction *cAct;
+
+private slots:
+ void clearFlags(){
+ resizeSide=NONE;
+ }
+
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *ev);
+ virtual void mousePressEvent(QMouseEvent *ev);
+ virtual void mouseReleaseEvent(QMouseEvent *ev);
+
+signals:
+ void MenuResized(QSize); //Emitted when the menu is manually resized by the user
+
+};
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaX11.cpp b/src-qt5/core/libLumina/LuminaX11.cpp
new file mode 100644
index 00000000..5d1ac6f3
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaX11.cpp
@@ -0,0 +1,2172 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LuminaX11.h"
+
+#include <QString>
+#include <QByteArray>
+#include <QFile>
+#include <QObject>
+#include <QImage>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QScreen>
+
+
+
+//XCB Library includes
+#include <xcb/xcb.h>
+#include <xcb/xcb_atom.h>
+#include <xcb/xproto.h>
+#include <xcb/xcb_ewmh.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/xcb_image.h>
+#include <xcb/xcb_aux.h>
+#include <xcb/composite.h>
+#include <xcb/damage.h>
+
+//XLib includes
+#include <X11/extensions/Xdamage.h>
+
+#define DEBUG 0
+
+//===============================
+//===============================
+// XCB LIBRARY FUNCTIONS
+//===============================
+//===============================
+LXCB::LXCB(){
+ xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH);
+ if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){
+ qDebug() << "Error with XCB atom initializations";
+ }else{
+ qDebug() << "Number of XCB screens:" << EWMH.nb_screens;
+ }
+}
+LXCB::~LXCB(){
+ xcb_ewmh_connection_wipe(&EWMH);
+}
+
+// private function
+void LXCB::createWMAtoms(){
+ ATOMS.clear();
+ atoms.clear();
+ //List the atoms needed by some WM functions
+ atoms << "WM_TAKE_FOCUS" << "WM_DELETE_WINDOW" << "WM_PROTOCOLS"; //WM_PROTOCOLS
+
+ //Create all the requests for the atoms
+ QList<xcb_intern_atom_reply_t*> reply;
+ for(int i=0; i<atoms.length(); i++){
+ reply << xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, atoms[i].length(), atoms[i].toLocal8Bit()), NULL);
+ }
+ //Now evaluate all the requests and save the atoms
+ for(int i=0; i<reply.length(); i++){
+ if(reply[i]!=0){
+ ATOMS << reply[i]->atom;
+ free(reply[i]); //done with this reply
+ }else{
+ //Invalid atom - could not be created
+ atoms.removeAt(i);
+ reply.removeAt(i);
+ i--;
+ }
+ }
+
+
+
+}
+
+// === WindowList() ===
+QList<WId> LXCB::WindowList(bool rawlist){
+ if(DEBUG){ qDebug() << "XCB: WindowList()" << rawlist; }
+ QList<WId> output;
+ //qDebug() << "Get Client list cookie";
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_unchecked( &EWMH, 0);
+ xcb_ewmh_get_windows_reply_t winlist;
+ //qDebug() << "Get client list";
+ if( 1 == xcb_ewmh_get_client_list_reply( &EWMH, cookie, &winlist, NULL) ){
+ //qDebug() << " - Loop over items";
+ unsigned int wkspace = CurrentWorkspace();
+ for(unsigned int i=0; i<winlist.windows_len; i++){
+ //Filter out the Lumina Desktop windows
+ if(WindowClass(winlist.windows[i]) == "Lumina Desktop Environment"){ continue; }
+ //Also filter out windows not on the active workspace
+ else if( (WindowWorkspace(winlist.windows[i])!=wkspace) && !rawlist ){ continue; }
+ else{
+ output << winlist.windows[i];
+ }
+ }
+ }
+ return output;
+}
+
+// === CurrentWorkspace() ===
+unsigned int LXCB::CurrentWorkspace(){
+ if(DEBUG){ qDebug() << "XCB: CurrentWorkspace()"; }
+ //qDebug() << "Get Current Workspace";
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop_unchecked(&EWMH, 0);
+ uint32_t wkspace = 0;
+ xcb_ewmh_get_current_desktop_reply(&EWMH, cookie, &wkspace, NULL);
+ //qDebug() << " - done:" << wkspace;
+ return wkspace;
+}
+
+unsigned int LXCB::NumberOfWorkspaces(){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_number_of_desktops_unchecked(&EWMH, 0);
+ uint32_t number;
+ if(1==xcb_ewmh_get_number_of_desktops_reply(&EWMH, cookie, &number, NULL) ){
+ return number;
+ }else{
+ return 0; //unable to get this property
+ }
+}
+
+// === ActiveWindow() ===
+WId LXCB::ActiveWindow(){
+ if(DEBUG){ qDebug() << "XCB: ActiveWindow()"; }
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window_unchecked(&EWMH, 0);
+ xcb_window_t actwin;
+ if(1 == xcb_ewmh_get_active_window_reply(&EWMH, cookie, &actwin, NULL) ){
+ return actwin;
+ }else{
+ return 0; //invalid ID/failure
+ }
+}
+
+// === CheckDisableXinerama() ===
+bool LXCB::CheckDisableXinerama(){
+ //returns true if Xinerama was initially set but now disabled
+ return false;
+ // TO-DO - not complete yet
+
+ /*xcb_query_extension_cookie_t cookie = xcb_query_extension_unchecked(QX11Info::connection(), 8, "Xinerama");
+ xcb_query_extension_reply_t *reply = xcb_query_extension_reply(QX11Info::connection(), cookie, NULL);
+
+ if(reply!=0){
+
+ free(reply);
+ }
+ */
+}
+
+// === RegisterVirtualRoots() ===
+void LXCB::RegisterVirtualRoots(QList<WId> roots){
+ if(DEBUG){ qDebug() << "XCB: RegisterVirtualRoots()"; }
+ //First convert the QList into the proper format
+ xcb_window_t *list = new xcb_window_t[ roots.length() ];
+ for(int i=0; i<roots.length(); i++){
+ list[i] = roots[i]; //move from the QList to the array
+ }
+ //Now set the property
+ xcb_ewmh_set_virtual_roots(&EWMH, 0, roots.length(), list);
+ //Now delete the temporary array from memory
+ delete [] list;
+}
+
+// ===== SetCurrentWorkspace() =====
+void LXCB::SetCurrentWorkspace(int number){
+ //Need to send a client message event for the window so the WM picks it up
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = QX11Info::appRootWindow();
+ event.type = EWMH._NET_CURRENT_DESKTOP;
+ event.data.data32[0] = number; //set to enabled
+ event.data.data32[1] = XCB_TIME_CURRENT_TIME;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+ //EWMH function (does not seem to be recognized by Fluxbox)
+ xcb_ewmh_request_change_showing_desktop(&EWMH, QX11Info::appScreen(), number);
+}
+
+// === WindowClass() ===
+QString LXCB::WindowClass(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowClass()" << win; }
+ QString out;
+ if(win==0){ return ""; }
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class_unchecked(QX11Info::connection(), win);
+ if(cookie.sequence == 0){ return out; }
+ xcb_icccm_get_wm_class_reply_t value;
+ if( 1== xcb_icccm_get_wm_class_reply( QX11Info::connection(), cookie, &value, NULL) ){
+ out = QString::fromUtf8(value.class_name);
+ xcb_icccm_get_wm_class_reply_wipe(&value);
+ }
+ return out;
+}
+
+// === WindowWorkspace() ===
+unsigned int LXCB::WindowWorkspace(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowWorkspace()" << win; }
+ //qDebug() << "Get Window Workspace";
+ if(win==0){ return 0; }
+ uint32_t wkspace = 0;
+ xcb_get_property_cookie_t scookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win);
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return wkspace; }
+ xcb_ewmh_get_wm_desktop_reply(&EWMH, cookie, &wkspace, NULL);
+ xcb_ewmh_get_atoms_reply_t reply;
+ if(1==xcb_ewmh_get_wm_state_reply(&EWMH,scookie, &reply, NULL)){
+ //Also check if this window is "sticky", in which case return the current workspace (on all of them)
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==EWMH._NET_WM_STATE_STICKY){ wkspace = LXCB::CurrentWorkspace(); break; }
+ }
+ }
+ //qDebug() << " - done: " << wkspace;
+ return wkspace;
+}
+
+// === WindowGeometry() ===
+QRect LXCB::WindowGeometry(WId win, bool includeFrame){
+ if(DEBUG){ qDebug() << "XCB: WindowGeometry()"; }
+ QRect geom;
+ if(win==0){ return geom; }
+ xcb_get_geometry_cookie_t cookie = xcb_get_geometry(QX11Info::connection(), win);
+ xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(QX11Info::connection(), cookie, NULL);
+ //qDebug() << "Get Window Geometry:" << reply;
+ if(reply != 0){
+ geom = QRect(0, 0, reply->width, reply->height); //make sure to use the origin point for the window
+ //qDebug() << " - "<<reply->x << reply->y << reply->width << reply->height;
+ free(reply);
+ if(includeFrame){
+ //Need to add/include the frame extents as well (assuming the frame info is available)
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win);
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_extents_reply_t frame;
+ if(1== xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &frame, NULL) ){
+ //adjust the origin point to account for the frame
+ geom.translate(-frame.left, -frame.top); //move to the orign point for the frame
+ //adjust the size (include the frame sizes)
+ geom.setWidth( geom.width() + frame.left + frame.right );
+ geom.setHeight( geom.height() + frame.top + frame.bottom );
+ }
+ //qDebug() << " - Frame:" << frame.left << frame.right << frame.top << frame.bottom;
+ //qDebug() << " - Modified with Frame:" << geom.x() << geom.y() << geom.width() << geom.height();
+ }
+ }
+ //Now need to convert this to absolute coordinates (not parent-relavitve)
+ xcb_translate_coordinates_cookie_t tcookie = xcb_translate_coordinates(QX11Info::connection(), win, QX11Info::appRootWindow(), geom.x(), geom.y());
+ xcb_translate_coordinates_reply_t *trans = xcb_translate_coordinates_reply(QX11Info::connection(), tcookie, NULL);
+ if(trans!=0){
+ //qDebug() << " - Got Translation:" << trans->dst_x << trans->dst_y;
+ //Replace the origin point with the global position (sizing remains the same)
+ geom.moveLeft(trans->dst_x); //adjust X coordinate (no size change)
+ geom.moveTop(trans->dst_y); //adjust Y coordinate (no size change)
+ free(trans);
+ }
+ }else{
+ //Need to do another catch for this situation (probably not mapped yet)
+ }
+ return geom;
+}
+
+// === WindowFrameGeometry() ===
+QList<int> LXCB::WindowFrameGeometry(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowFrameGeometry()"; }
+ //Returns: [top, bottom, left, right] sizes for the frame
+ QList<int> geom;
+ if(win!=0){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win);
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_extents_reply_t frame;
+ if(1== xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &frame, NULL) ){
+ //adjust the origin point to account for the frame
+ geom << frame.top << frame.bottom << frame.left << frame.right;
+ }
+ }
+ }
+ if(geom.isEmpty()){ geom << 0 << 0 << 0 << 0; }
+ return geom;
+}
+
+// === WindowState() ===
+LXCB::WINDOWVISIBILITY LXCB::WindowState(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowState()"; }
+ if(win==0){ return IGNORE; }
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return IGNORE; }
+ xcb_ewmh_get_atoms_reply_t states;
+ WINDOWVISIBILITY cstate = IGNORE;
+ //First Check for special states (ATTENTION in particular);
+ if( 1 == xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &states, NULL) ){
+ for(unsigned int i=0; i<states.atoms_len; i++){
+ if(states.atoms[i] == EWMH._NET_WM_STATE_DEMANDS_ATTENTION){ cstate = ATTENTION; break; } //nothing more urgent - stop here
+ else if(states.atoms[i] == EWMH._NET_WM_STATE_HIDDEN){ cstate = INVISIBLE; }
+ }
+ }
+ //Now check to see if the window is the active one
+ if(cstate == IGNORE){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window_unchecked(&EWMH, 0);
+ xcb_window_t actwin;
+ if(1 == xcb_ewmh_get_active_window_reply(&EWMH, cookie, &actwin, NULL) ){
+ if(actwin == win){ cstate = ACTIVE; }
+ }
+ }
+ //Now check for ICCCM Urgency hint (not sure if this is still valid with EWMH instead)
+ /*if(cstate == IGNORE){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(QX11Info::connection(), win);
+ xcb_icccm_wm_hints_t hints;
+ if( 1== xcb_icccm_get_wm_hints_reply(QX11Info::connection(), cookie, &hints, NULL) ){
+ if(xcb_icccm_wm_hints_get_urgency(hints) ){ cstate = ATTENTION; };
+ }
+ }*/
+ //Now check for standard visible/invisible attribute (current mapping state)
+ if(cstate == IGNORE){
+ xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), win);
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL);
+ if(attr!=0){
+ if(attr->map_state==XCB_MAP_STATE_VIEWABLE){ cstate = VISIBLE; }
+ else{ cstate = INVISIBLE; }
+ free(attr);
+ }
+ }
+ return cstate;
+}
+
+// === WindowVisibleIconName() ===
+QString LXCB::WindowVisibleIconName(WId win){ //_NET_WM_VISIBLE_ICON_NAME
+ if(DEBUG){ qDebug() << "XCB: WindowVisibleIconName()"; }
+ if(win==0){ return ""; }
+ QString out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return out; }
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_visible_icon_name_reply(&EWMH, cookie, &data, NULL) ){
+ out = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ return out;
+}
+
+// === WindowIconName() ===
+QString LXCB::WindowIconName(WId win){ //_NET_WM_ICON_NAME
+ if(DEBUG){ qDebug() << "XCB: WindowIconName()"; }
+ if(win==0){ return ""; }
+ QString out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return out; }
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_icon_name_reply(&EWMH, cookie, &data, NULL) ){
+ out = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ return out;
+}
+
+// === WindowVisibleName() ===
+QString LXCB::WindowVisibleName(WId win){ //_NET_WM_VISIBLE_NAME
+ if(DEBUG){ qDebug() << "XCB: WindowVisibleName()"; }
+ if(win==0){ return ""; }
+ QString out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return out; }
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_visible_name_reply(&EWMH, cookie, &data, NULL) ){
+ out = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ return out;
+}
+
+// === WindowName() ===
+QString LXCB::WindowName(WId win){ //_NET_WM_NAME
+ if(DEBUG){ qDebug() << "XCB: WindowName()"; }
+ if(win==0){ return ""; }
+ QString out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return out; }
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_name_reply(&EWMH, cookie, &data, NULL) ){
+ out = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ return out;
+}
+
+// === OldWindowName() ===
+QString LXCB::OldWindowName(WId win){ //WM_NAME (old standard)
+ if(DEBUG){ qDebug() << "XCB: OldWindowName()"; }
+ if(win==0){ return ""; }
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 == xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ QString name = QString::fromLocal8Bit(reply.name);
+ xcb_icccm_get_text_property_reply_wipe(&reply);
+ return name;
+ }else{
+ return "";
+ }
+}
+
+// === OldWindowIconName() ===
+QString LXCB::OldWindowIconName(WId win){ //WM_ICON_NAME (old standard)
+ if(DEBUG){ qDebug() << "XCB: OldWindowIconName()"; }
+ if(win==0){ return ""; }
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 == xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ QString name = QString::fromLocal8Bit(reply.name);
+ xcb_icccm_get_text_property_reply_wipe(&reply);
+ return name;
+ }else{
+ return "";
+ }
+}
+
+// === WindowIsMaximized() ===
+bool LXCB::WindowIsMaximized(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowIsMaximized()"; }
+ if(win==0){ return ""; }
+ //See if the _NET_WM_STATE_MAXIMIZED_[VERT/HORZ] flags are set on the window
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return false; }
+ xcb_ewmh_get_atoms_reply_t states;
+ if( 1 == xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &states, NULL) ){
+ //Loop over the states
+ for(unsigned int i=0; i<states.atoms_len; i++){
+ if(states.atoms[i] == EWMH._NET_WM_STATE_MAXIMIZED_HORZ \
+ || states.atoms[i] == EWMH._NET_WM_STATE_MAXIMIZED_VERT ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// === WindowIsFullscreen() ===
+int LXCB::WindowIsFullscreen(WId win){
+ if(DEBUG){ qDebug() << "XCB: WindowIsFullscreen()"; }
+ if(win==0){ return -1; }
+ //bool fullS = false;
+ //See if the _NET_WM_STATE_FULLSCREEN flag is set on the window
+ /*xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win);
+ if(cookie.sequence == 0){ return false; }
+ xcb_ewmh_get_atoms_reply_t states;
+ if( 1 == xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &states, NULL) ){
+ //Loop over the states
+ for(unsigned int i=0; i<states.atoms_len; i++){
+ if(states.atoms[i] == EWMH._NET_WM_STATE_FULLSCREEN){
+ fullS = true;
+ break;
+ }
+ }
+ }*/
+ //if(!fullS){
+ //Fallback check for windows which are painted above everything else
+ // but don't have the FULLSCREEN flag set (even though they are technically full-screen)
+ int fscreen = -1;
+ //qDebug() << "FALLBACK FULLSCREEN CHECK:";
+ QRect geom = LXCB::WindowGeometry(win, false);
+ QDesktopWidget *desk = QApplication::desktop();
+ for(int i=0; i<desk->screenCount(); i++){
+ QRect sgeom = desk->screenGeometry(i);
+ qDebug() << " -- Check Window Geom:" << sgeom << geom << this->WindowClass(win);
+ if( sgeom.contains(geom.center()) ){
+ //Allow a 1 pixel variation in "full-screen" detection
+ qDebug() << " -- Found Screen:" << i;
+ if( geom.width() >= (sgeom.width()-1) && geom.height()>=(sgeom.height()-1) ){
+ qDebug() << " -- Is Fullscreen!";
+ //fullS = true;
+ fscreen = i;
+ }
+ break; //found the screen which contains this window
+ }
+ }
+ //}
+ //return fullS;
+ return fscreen;
+}
+
+// === WindowIcon() ===
+QIcon LXCB::WindowIcon(WId win){
+ //Fetch the _NET_WM_ICON for the window and return it as a QIcon
+ if(DEBUG){ qDebug() << "XCB: WindowIcon()"; }
+ QIcon icon;
+ if(win==0){ return icon; }
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&EWMH, win);
+ xcb_ewmh_get_wm_icon_reply_t reply;
+ if(1 == xcb_ewmh_get_wm_icon_reply(&EWMH, cookie, &reply, NULL)){
+ xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply);
+ //Just use the first
+ bool done =false;
+ while(!done){
+ //Now convert the current data into a Qt image
+ // - first 2 elements are width and height (removed via XCB functions)
+ // - data in rows from left to right and top to bottom
+ QImage image(iter.width, iter.height, QImage::Format_ARGB32); //initial setup
+ uint* dat = iter.data;
+ //dat+=2; //remember the first 2 element offset
+ for(int i=0; i<image.byteCount()/4; ++i, ++dat){
+ ((uint*)image.bits())[i] = *dat;
+ }
+ icon.addPixmap(QPixmap::fromImage(image)); //layer this pixmap onto the icon
+ //Now see if there are any more icons available
+ done = (iter.rem<1); //number of icons remaining
+ if(!done){ xcb_ewmh_get_wm_icon_next(&iter); } //get the next icon data
+ }
+ xcb_ewmh_get_wm_icon_reply_wipe(&reply);
+ }
+ return icon;
+}
+
+// === SelectInput() ===
+void LXCB::SelectInput(WId win, bool isEmbed){
+ uint32_t mask;
+ if(isEmbed){
+ mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE;
+ }else{
+ mask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE;
+ }
+ xcb_change_window_attributes(QX11Info::connection(), win, XCB_CW_EVENT_MASK, &mask );
+}
+
+// === GenerateDamageID() ===
+uint LXCB::GenerateDamageID(WId win){
+ //Now create/register the damage handler
+ xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer
+ xcb_damage_create(QX11Info::connection(), dmgID, win, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
+ return ( (uint) dmgID );
+}
+
+
+// === SetAsSticky() ===
+void LXCB::SetAsSticky(WId win){
+ if(DEBUG){ qDebug() << "XCB: SetAsSticky()"; }
+ if(win==0){ return; }
+ //Need to send a client message event for the window so the WM picks it up
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = EWMH._NET_WM_STATE;
+ event.data.data32[0] = 1; //set to enabled
+ event.data.data32[1] = EWMH._NET_WM_STATE_STICKY;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+ //This method changes the property on the window directly - the WM is not aware of it
+ /*xcb_change_property( QX11Info::connection(), XCB_PROP_MODE_APPEND, win, EWMH._NET_WM_STATE, XCB_ATOM_ATOM, 32, 1, &(EWMH._NET_WM_STATE_STICKY) );
+ xcb_flush(QX11Info::connection()); //apply it right away*/
+}
+
+// === SetDisableWMActions() ===
+void LXCB::SetDisableWMActions(WId win){
+ if(DEBUG){ qDebug() << "XCB: SetDisableWMActions()"; }
+ //This disables all the various control that a WM allows for the window (except for allowing the "Sticky" state)
+ xcb_atom_t list[1];
+ list[0] = EWMH._NET_WM_ACTION_STICK;
+ xcb_ewmh_set_wm_allowed_actions(&EWMH, win, 1, list);
+}
+
+// === SetAsPanel() ===
+void LXCB::SetAsPanel(WId win){
+ if(DEBUG){ qDebug() << "XCB: SetAsPanel()"; }
+ if(win==0){ return; }
+ SetDisableWMActions(win); //also need to disable WM actions for this window
+ //Disable Input focus (panel activation ruins task manager window detection routines)
+ // - Disable Input flag in WM_HINTS
+ xcb_icccm_wm_hints_t hints;
+ //qDebug() << " - Disable WM_HINTS input flag";
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(QX11Info::connection(), win);
+ //qDebug() << " -- got cookie";
+ if(1 == xcb_icccm_get_wm_hints_reply(QX11Info::connection(), cookie, &hints, NULL) ){
+ //qDebug() << " -- Set no inputs flag";
+ xcb_icccm_wm_hints_set_input(&hints, false); //set no input focus
+ xcb_icccm_set_wm_hints(QX11Info::connection(), win, &hints); //save hints back to window
+ }
+ // - Remove WM_TAKE_FOCUS from the WM_PROTOCOLS for the window
+ // - - Generate the necessary atoms
+ //qDebug() << " - Generate WM_PROTOCOLS and WM_TAKE_FOCUS atoms";
+ xcb_atom_t WM_PROTOCOLS, WM_TAKE_FOCUS; //the two atoms needed
+ xcb_intern_atom_reply_t *preply = xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, 12, "WM_PROTOCOLS"), NULL);
+ xcb_intern_atom_reply_t *freply = xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, 13, "WM_TAKE_FOCUS"), NULL);
+ bool gotatoms = false;
+ if(preply && freply){
+ WM_PROTOCOLS = preply->atom;
+ WM_TAKE_FOCUS = freply->atom;
+ free(preply);
+ free(freply);
+ gotatoms = true;
+ //qDebug() << " -- success";
+ }
+ // - - Now update the protocols for the window
+ if(gotatoms){ //requires the atoms
+ //qDebug() << " - Get WM_PROTOCOLS";
+ xcb_icccm_get_wm_protocols_reply_t proto;
+ if( 1 == xcb_icccm_get_wm_protocols_reply(QX11Info::connection(), \
+ xcb_icccm_get_wm_protocols_unchecked(QX11Info::connection(), win, WM_PROTOCOLS), \
+ &proto, NULL) ){
+
+ //Found the current protocols, see if it has the focus atom set
+ //remove the take focus atom and re-save them
+ bool needremove = false;
+ //Note: This first loop is required so that we can initialize the modified list with a valid size
+ //qDebug() << " -- Check current protocols";
+ for(unsigned int i=0; i<proto.atoms_len; i++){
+ if(proto.atoms[i] == WM_TAKE_FOCUS){ needremove = true; break;}
+ }
+ if(needremove){
+ //qDebug() << " -- Remove WM_TAKE_FOCUS protocol";
+ xcb_atom_t *protolist = new xcb_atom_t[proto.atoms_len-1];
+ int num = 0;
+ for(unsigned int i=0; i<proto.atoms_len; i++){
+ if(proto.atoms[i] != WM_TAKE_FOCUS){
+ protolist[num] = proto.atoms[i];
+ num++;
+ }
+ }
+ //qDebug() << " -- Re-save modified protocols";
+ xcb_icccm_set_wm_protocols(QX11Info::connection(), win, WM_PROTOCOLS, num, protolist);
+ }
+ //qDebug() << " -- Clear protocols reply";
+ xcb_icccm_get_wm_protocols_reply_wipe(&proto);
+ }//end of get protocols check
+ } //end of gotatoms check
+ //Make sure it has the "dock" window type
+ // - get the current window types (Not necessary, only 1 type of window needed)
+
+ // - set the adjusted window type(s)
+ //qDebug() << " - Adjust window type";
+ xcb_atom_t list[1];
+ list[0] = EWMH._NET_WM_WINDOW_TYPE_DOCK;
+ xcb_ewmh_set_wm_window_type(&EWMH, win, 1, list);
+
+ //Make sure it is on all workspaces
+ //qDebug() << " - Set window as sticky";
+ SetAsSticky(win);
+
+}
+
+// === SetAsDesktop() ===
+void LXCB::SetAsDesktop(WId win){
+ if(DEBUG){ qDebug() << "XCB: SetAsDesktop()"; }
+ if(win==0){ return; }
+ SetDisableWMActions(win); //also need to disable WM actions for this window
+ xcb_atom_t list[1];
+ list[0] = EWMH._NET_WM_WINDOW_TYPE_DESKTOP;
+ xcb_ewmh_set_wm_window_type(&EWMH, win, 1, list);
+}
+
+// === CloseWindow() ===
+void LXCB::CloseWindow(WId win){
+ if(DEBUG){ qDebug() << "XCB: CloseWindow()"; }
+ if(win==0){ return; }
+ //This will close the specified window (might not close the entire application)
+ xcb_ewmh_request_close_window(&EWMH, 0, win, QX11Info::getTimestamp(), XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER);
+}
+
+// === KillClient() ===
+void LXCB::KillClient(WId win){
+ if(DEBUG){ qDebug() << "XCB: KillClient()"; }
+ if(win==0){ return; }
+ //This will forcibly close the application which created WIN
+ xcb_kill_client(QX11Info::connection(), win);
+}
+
+// === MinimizeWindow() ===
+void LXCB::MinimizeWindow(WId win){ //request that the window be unmapped/minimized
+ if(DEBUG){ qDebug() << "XCB: MinimizeWindow()"; }
+ if(win==0){ return; }
+ //Note: Fluxbox completely removes this window from the open list if unmapped manually
+ // xcb_unmap_window(QX11Info::connection(), win);
+ //xcb_flush(QX11Info::connection()); //make sure the command is sent out right away
+
+ //Need to send a client message event for the window so the WM picks it up
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = EWMH._NET_WM_STATE;
+ event.data.data32[0] = 1; //set to toggle (switch back and forth)
+ event.data.data32[1] = EWMH._NET_WM_STATE_HIDDEN;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+}
+
+// === ActivateWindow() ===
+void LXCB::ActivateWindow(WId win){ //request that the window become active
+ if(DEBUG){ qDebug() << "XCB: ActivateWindow();"; }
+ if(win==0){ return; }
+ //First need to get the currently active window
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window_unchecked(&EWMH, 0);
+ xcb_window_t actwin;
+ if(1 != xcb_ewmh_get_active_window_reply(&EWMH, cookie, &actwin, NULL) ){
+ actwin = 0;
+ }
+ if(actwin == win){ return; } //requested window is already active
+
+//Need to send a client message event for the window so the WM picks it up
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win; //window to activate
+ event.type = EWMH._NET_ACTIVE_WINDOW;
+ event.data.data32[0] = 2; //pager/direct user interaction
+ event.data.data32[1] = QX11Info::getTimestamp(); //current timestamp
+ event.data.data32[2] = actwin; //currently active window (0 if none)
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+}
+
+// ===== RestoreWindow() =====
+void LXCB::RestoreWindow(WId win){
+ uint32_t val = XCB_STACK_MODE_ABOVE;
+ xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_STACK_MODE, &val); //raise it
+ xcb_map_window(QX11Info::connection(), win); //map it
+}
+
+// === MaximizeWindow() ===
+void LXCB::MaximizeWindow(WId win, bool flagsonly){ //request that the window become maximized
+ if(DEBUG){ qDebug() << "XCB: MaximizeWindow()"; }
+ if(win==0){ return; }
+ if(flagsonly){
+ //Directly set the flags on the window (bypassing the WM)
+ xcb_atom_t list[2];
+ list[0] = EWMH._NET_WM_STATE_MAXIMIZED_VERT;
+ list[1] = EWMH._NET_WM_STATE_MAXIMIZED_HORZ;
+ xcb_ewmh_set_wm_state(&EWMH, win, 2, list);
+
+ }else{
+ //Need to send a client message event for the window so the WM picks it up
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = EWMH._NET_WM_STATE;
+ event.data.data32[0] = 2; //set to toggle (switch back and forth)
+ event.data.data32[1] = EWMH._NET_WM_STATE_MAXIMIZED_VERT;
+ event.data.data32[2] = EWMH._NET_WM_STATE_MAXIMIZED_HORZ;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ }
+}
+
+// === MoveResizeWindow() ===
+void LXCB::MoveResizeWindow(WId win, QRect geom){
+ if(DEBUG){ qDebug() << "XCB: MoveResizeWindow()"; }
+ if(win==0){ return; }
+ //NOTE: geom needs to be in root/absolute coordinates!
+ //qDebug() << "MoveResize Window:" << geom.x() << geom.y() << geom.width() << geom.height();
+
+ //Move the window
+ /*xcb_ewmh_request_moveresize_window(&EWMH, 0, win, XCB_GRAVITY_STATIC, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER, \
+ XCB_EWMH_MOVERESIZE_WINDOW_X | XCB_EWMH_MOVERESIZE_WINDOW_Y | XCB_MOVERESIZE_WINDOW_WIDTH | XCB_MOVERESIZE_WINDOW_HEIGHT, \
+ geom.x(), geom.y(), geom.width(), geom.height());*/
+
+ //Use the basic XCB functions instead of ewmh (Issues with combining the XCB_EWMH_MOVERESIZE _*flags)
+ uint32_t values[4];
+ values[0] = geom.x(); values[1] = geom.y();
+ values[2] = geom.width(); values[3] = geom.height();
+ xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+
+}
+
+// ===== ResizeWindow() =====
+void LXCB::ResizeWindow(WId win, int width, int height){
+ //Use the basic XCB functions instead of ewmh
+ uint32_t values[] = {width, height};
+ xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+}
+
+// === ReserveLocation ===
+void LXCB::ReserveLocation(WId win, QRect geom, QString loc){
+ loc = loc.toLower().simplified();
+ //Put the values in the proper structures
+ xcb_ewmh_wm_strut_partial_t LOC;
+ //Initialize the structure to zeros
+ LOC.left = LOC.right = LOC.top = LOC.bottom = 0; //initial setting
+ LOC.left_start_y = LOC.left_end_y = LOC.right_start_y = LOC.right_end_y = 0;
+ LOC.top_start_x = LOC.top_end_x = LOC.bottom_start_x = LOC.bottom_end_x = 0;
+ //Now put the values into the structure based on location
+ if(loc=="top"){
+ //top of screen
+ LOC.top = geom.height(); //top width
+ LOC.top_start_x = geom.x(); //top x start
+ LOC.top_end_x = geom.x()+geom.width(); //top x end
+ }else if(loc=="bottom"){
+ //bottom of screen
+ LOC.bottom = geom.height(); //bottom width
+ LOC.bottom_start_x = geom.x(); //bottom x start
+ LOC.bottom_end_x = geom.x()+geom.width(); //bottom x end
+ }else if(loc=="left"){
+ LOC.left = geom.width();
+ LOC.left_start_y = geom.y();
+ LOC.left_end_y = geom.y()+geom.height();
+ }else{ //right
+ LOC.right = geom.width();
+ LOC.right_start_y = geom.y();
+ LOC.right_end_y = geom.y()+geom.height();
+ }
+
+ //Change the property
+ xcb_ewmh_set_wm_strut_partial(&EWMH, win, LOC); //_NET_WM_STRUT_PARTIAL (not always used)
+ xcb_ewmh_set_wm_strut(&EWMH, win, LOC.left, LOC.right, LOC.top, LOC.bottom); //_NET_WM_STRUT
+}
+
+/*void LXCB::SetWindowBackground(QWidget *parent, QRect area, WId client){
+ //Copy the image from the parent onto the client (parent/child - for system tray apps)
+ //First create the background graphics context
+ //qDebug() << "Create graphics context";
+ //xcb_screen_t *root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen());
+ uint32_t val = XCB_GX_CLEAR;
+ xcb_gcontext_t graphic_context = xcb_generate_id(QX11Info::connection());
+ xcb_create_gc(QX11Info::connection(), graphic_context, client, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, &val);
+ //qDebug() << "Copy Background Area";
+ //Now copy the image onto the client background
+ xcb_copy_area(QX11Info::connection(),
+ parent->winId(),
+ client,
+ graphic_context,
+ area.x(), area.y(),
+ 0, 0,
+ area.width(), area.height());
+ //Now re-map the client so it paints on top of the new background
+ //qDebug() << "Map Window";
+ //xcb_map_window(QX11Info::connection(), client);
+ //Clean up variables
+ xcb_free_gc(QX11Info::connection(), graphic_context);
+}*/
+
+// === EmbedWindow() ===
+uint LXCB::EmbedWindow(WId win, WId container){
+ if(DEBUG){ qDebug() << "XCB: EmbedWindow()"; }
+ //This returns the damage control ID number (or 0 for a failure)
+ if(win==0 || container==0 || LXCB::WindowClass(win).isEmpty() ){ return 0; } //invalid window (destroyed before getting here?)
+ //qDebug() << "Embed Window:" << win << container;
+
+ //Initialize any atoms that will be needed
+ xcb_intern_atom_cookie_t ecookie = xcb_intern_atom_unchecked(QX11Info::connection(), 0, 7, "_XEMBED");
+
+ xcb_intern_atom_reply_t *ereply = xcb_intern_atom_reply(QX11Info::connection(), ecookie, NULL);
+ if(ereply==0){ return 0; } //unable to initialize the atom
+ xcb_atom_t emb = ereply->atom;
+ free(ereply); //done with this structure
+
+ //Reparent the window into the container
+ xcb_reparent_window(QX11Info::connection(), win, container, 0, 0);
+ xcb_map_window(QX11Info::connection(), win);
+
+ //Now send the embed event to the app
+ //qDebug() << " - send _XEMBED event";
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = emb; //_XEMBED
+ event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY
+ event.data.data32[2] = 0;
+ event.data.data32[3] = container; //WID of the container
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+ //Now setup any redirects and return
+ this->SelectInput(win, true); //Notify of structure changes
+ xcb_composite_redirect_window(QX11Info::connection(), win, XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
+
+ //Now map the window (will be a transparent child of the container)
+ xcb_map_window(QX11Info::connection(), win);
+
+ //Now create/register the damage handler
+ // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore)
+ //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer
+ //xcb_damage_create(QX11Info::connection(), dmgID, win, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
+ // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself).
+ Damage dmgID = XDamageCreate(QX11Info::display(), win, XDamageReportRawRectangles);
+
+ //qDebug() << " - Done";
+ return ( (uint) dmgID );
+}
+
+// === Unembed Window() ===
+bool LXCB::UnembedWindow(WId win){
+ if(DEBUG){ qDebug() << "XCB: UnembedWindow()"; }
+ if(win==0){ return false; }
+ //Remove redirects
+ uint32_t val[] = {XCB_EVENT_MASK_NO_EVENT};
+ xcb_change_window_attributes(QX11Info::connection(), win, XCB_CW_EVENT_MASK, val);
+ //Make sure it is invisible
+ xcb_unmap_window(QX11Info::connection(), win);
+ //Reparent the window back to the root window
+ xcb_reparent_window(QX11Info::connection(), win, QX11Info::appRootWindow(), 0, 0);
+ return true;
+}
+
+// === TrayImage() ===
+QPixmap LXCB::TrayImage(WId win){
+ QPixmap pix;
+
+ //Get the current QScreen (for XCB->Qt conversion)
+ QList<QScreen*> scrnlist = QApplication::screens();
+ if(scrnlist.isEmpty()){ return pix; }
+
+ //Try to grab the given window directly with Qt
+ if(pix.isNull()){
+ pix = scrnlist[0]->grabWindow(win);
+ }
+ return pix;
+
+ //NOTE: Code below here saved for reference later (as necessary)
+ // -------------------------------
+ /*//First get the pixmap from the XCB compositing layer (since the tray images are redirected there)
+ xcb_pixmap_t pixmap = xcb_generate_id(QX11Info::connection());
+ xcb_composite_name_window_pixmap(QX11Info::connection(), win, pixmap);
+ //Get the sizing information about the pixmap
+ xcb_get_geometry_cookie_t Gcookie = xcb_get_geometry_unchecked(QX11Info::connection(), pixmap);
+ xcb_get_geometry_reply_t *Greply = xcb_get_geometry_reply(QX11Info::connection(), Gcookie, NULL);
+ if(Greply==0){ qDebug() << "[Tray Image] - Geom Fetch Error:"; return QPixmap(); } //Error in geometry detection
+
+ //Now convert the XCB pixmap into an XCB image
+ xcb_get_image_cookie_t GIcookie = xcb_get_image_unchecked(QX11Info::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, 0, 0, Greply->width, Greply->height, 0xffffffff);
+ xcb_get_image_reply_t *GIreply = xcb_get_image_reply(QX11Info::connection(), GIcookie, NULL);
+ if(GIreply==0){ qDebug() << "[Tray Image] - Image Convert Error:"; return QPixmap(); } //Error in conversion
+ uint8_t *GIdata = xcb_get_image_data(GIreply);
+ uint32_t BPL = xcb_get_image_data_length(GIreply) / Greply->height; //bytes per line
+ //Now convert the XCB image into a Qt Image
+ QImage image(const_cast<uint8_t *>(GIdata), Greply->width, Greply->height, BPL, QImage::Format_ARGB32_Premultiplied);
+ //Free the various data structures
+ free(GIreply); //done with get image reply
+ xcb_free_pixmap(QX11Info::connection(), pixmap); //done with the raw pixmap
+ free(Greply); //done with geom reply*/
+
+ /* NOTE: Found these little bit in the Qt sources - not sure if it is needed, but keep it here for reference
+ // we may have to swap the byte order based on system type
+ uint8_t image_byte_order = connection->setup()->image_byte_order;
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST))
+ {
+ for (int i=0; i < image.height(); i++) {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ }
+ }*/
+
+ // fix-up alpha channel
+ /*if (image.format() == QImage::Format_RGB32) {
+ QRgb *p = (QRgb *)image.bits();
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x)
+ p[x] |= 0xff000000;
+ p += bytes_per_line / 4;
+ }*/
+
+ //Convert the QImage into a QPixmap and return it
+ //return QPixmap::fromImage(image.copy());
+}
+
+// ===== startSystemTray() =====
+WId LXCB::startSystemTray(int screen){
+ qDebug() << "Starting System Tray:" << screen;
+ //Setup the freedesktop standards compliance
+
+ //Get the appropriate atom for this screen
+ QString str = QString("_NET_SYSTEM_TRAY_S%1").arg(QString::number(screen));
+ //qDebug() << "Default Screen Atom Name:" << str;
+ xcb_intern_atom_reply_t *treply = xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, str.length(), str.toLocal8Bit()), NULL);
+ xcb_intern_atom_reply_t *oreply = xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, 28, "_NET_SYSTEM_TRAY_ORIENTATION"), NULL);
+ xcb_intern_atom_reply_t *vreply = xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, 23, "_NET_SYSTEM_TRAY_VISUAL"), NULL);
+ if(treply==0){
+ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_S<num> atom";
+ return 0;
+ }
+ if(oreply==0){
+ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_ORIENTATION atom";
+ return 0;
+ }
+ if(vreply==0){
+ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_VISUAL atom";
+ return 0;
+ }
+ xcb_atom_t _NET_SYSTEM_TRAY_S = treply->atom;
+ xcb_atom_t _NET_SYSTEM_TRAY_ORIENTATION = oreply->atom;
+ xcb_atom_t _NET_SYSTEM_TRAY_VISUAL = vreply->atom;
+ free(treply); //done with atom generation
+ free(oreply);
+ free(vreply);
+
+ //Make sure that there is no other system tray running
+ xcb_get_selection_owner_reply_t *ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \
+ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL);
+ if(ownreply==0){
+ qWarning() << " - Could not get owner selection reply";
+ return 0;
+ }
+ if(ownreply->owner != 0){
+ free(ownreply);
+ qWarning() << " - An alternate system tray is currently in use";
+ return 0;
+ }
+ free(ownreply);
+
+ //Create a simple window to register as the tray (not visible - just off the screen)
+ xcb_screen_t *root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen());
+ uint32_t params[] = {1};
+ WId LuminaSessionTrayID = xcb_generate_id(QX11Info::connection()); //need a new ID
+ xcb_create_window(QX11Info::connection(), root_screen->root_depth, \
+ LuminaSessionTrayID, root_screen->root, -1, -1, 1, 1, 0, \
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \
+ XCB_CW_OVERRIDE_REDIRECT, params);
+
+ //Now register this widget as the system tray
+ xcb_set_selection_owner(QX11Info::connection(), LuminaSessionTrayID, _NET_SYSTEM_TRAY_S, XCB_CURRENT_TIME);
+ //Make sure that it was registered properly
+ ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \
+ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL);
+
+ if(ownreply==0 || ownreply->owner != LuminaSessionTrayID){
+ if(ownreply!=0){ free(ownreply); }
+ qWarning() << " - Could not register the system tray";
+ xcb_destroy_window(QX11Info::connection(), LuminaSessionTrayID);
+ return 0;
+ }
+ free(ownreply); //done with structure
+
+ //Now register the orientation of the system tray
+ uint32_t orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, LuminaSessionTrayID, \
+ _NET_SYSTEM_TRAY_ORIENTATION, XCB_ATOM_CARDINAL, 32, 1, &orient);
+
+ //Now set the visual ID for the system tray (same as the root window, but TrueColor)
+ xcb_visualtype_t *type = xcb_aux_find_visual_by_attrs(root_screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32);
+ if(type!=0){
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, LuminaSessionTrayID, \
+ _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, &type->visual_id);
+ }else{
+ qWarning() << " - Could not set TrueColor visual for system tray";
+ }
+
+ //Finally, send out an X event letting others know that the system tray is up and running
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = root_screen->root;
+ event.type = EWMH.MANAGER; //MANAGER atom
+ event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[1] = _NET_SYSTEM_TRAY_S; //_NET_SYSTEM_TRAY_S atom
+ event.data.data32[2] = LuminaSessionTrayID;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, root_screen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+ //Success
+ return LuminaSessionTrayID;
+}
+
+// ===== closeSystemTray() =====
+void LXCB::closeSystemTray(WId trayID){
+ xcb_destroy_window(QX11Info::connection(), trayID);
+}
+
+// === SetScreenWorkArea() ===
+/*void LXCB::SetScreenWorkArea(unsigned int screen, QRect rect){
+ //This is only useful because Fluxbox does not set the _NET_WORKAREA root atom
+ // This needs to be better incorporated into the new window manager later
+
+ //First get the current workarea array (for all monitors/screens)
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_workarea_unchecked(&EWMH, 0);
+ xcb_ewmh_get_workarea_reply_t work;
+ if(0==xcb_ewmh_get_workarea_reply(&EWMH, cookie, &work, NULL)){ return; } //Error: Could not retrieve current work areas
+ //Save what we need only from the reply
+ unsigned int desks = work.workarea_len;
+ if(desks <= screen){ return; } //invalid screen to modify
+ qDebug() << "Number of desktops/screens:" << desks;
+ xcb_ewmh_geometry_t *dareas = work.workarea;
+ //Adjust the work area for the input monitor/screen
+ dareas[screen].x = rect.x();
+ dareas[screen].y = rect.y();
+ dareas[screen].width = rect.width();
+ dareas[screen].height = rect.height();
+ //Now save the array again
+ xcb_ewmh_set_workarea(&EWMH, 0, desks, dareas); //_NET_WORKAREA
+ //Make sure to clear that reply
+ xcb_ewmh_get_workarea_reply_wipe(&work);
+}*/
+
+//============
+// WM Functions (directly changing properties/settings)
+// - Using these directly may prevent the WM from seeing the change
+//============
+void LXCB::WM_CloseWindow(WId win, bool force){
+
+ if(!force){ // && WM_ICCCM_GetProtocols(win).testFlag(LXCB::DELETE_WINDOW)){
+ //Send the window a WM_DELETE_WINDOW message
+ if(atoms.isEmpty()){ createWMAtoms(); } //need these atoms
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = ATOMS[atoms.indexOf("WM_PROTOCOLS")];
+ event.data.data32[0] = ATOMS[atoms.indexOf("WM_DELETE_WINDOW")];
+ event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ xcb_flush(QX11Info::connection());
+ }else{ xcb_destroy_window(QX11Info::connection(), win); }
+
+}
+
+void LXCB::WM_ShowWindow(WId win){
+ xcb_map_window(QX11Info::connection(), win);
+}
+
+void LXCB::WM_HideWindow(WId win){
+ xcb_unmap_window(QX11Info::connection(), win);
+}
+
+QList<WId> LXCB::WM_RootWindows(){
+ xcb_query_tree_cookie_t cookie = xcb_query_tree(QX11Info::connection(), QX11Info::appRootWindow());
+ xcb_query_tree_reply_t *reply = 0;
+ QList<WId> out;
+ reply=xcb_query_tree_reply(QX11Info::connection(), cookie, NULL);
+ if(reply!=0){
+ int num = xcb_query_tree_children_length(reply);
+ xcb_window_t *children = xcb_query_tree_children(reply);
+ for(int i=0; i<num; i++){
+ if(!out.contains(children[i])){ out << children[i]; }
+ }
+ free(reply);
+ }
+ return out;
+}
+
+WId LXCB::WM_CreateWindow(WId parent){
+ if(parent ==0){ parent = QX11Info::appRootWindow(); }
+ xcb_screen_t *root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen());
+ uint32_t params[] = {1};
+ WId win = xcb_generate_id(QX11Info::connection()); //need a new ID
+ xcb_create_window(QX11Info::connection(), root_screen->root_depth, \
+ win, parent, -1, -1, 1, 1, 0, \
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \
+ XCB_CW_OVERRIDE_REDIRECT, params);
+ return win;
+}
+
+bool LXCB::WM_ManageWindow(WId win, bool needsmap){
+#define CLIENT_WIN_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_FOCUS_CHANGE)
+ //return whether the window is/should be managed
+ if(WM_ICCCM_GetClass(win).isEmpty() ){ return false; }
+ xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), win);
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL);
+ if(attr == 0){ return false; } //could not get attributes of window
+ if(attr->override_redirect){ free(attr); return false; } //window has override redirect set (do not manage)
+ if(!needsmap && attr->map_state != XCB_MAP_STATE_VIEWABLE){
+ //window is never supposed to be visible (lots of these)
+ //if( !WM_ICCCM_GetClass(win).contains("xterm") ){ //Some windows mis-set this flag
+ qDebug() << " - Not Viewable.." << WM_ICCCM_GetClass(win);
+ free(attr); return false;
+ //}
+ }
+ //Setup event handling on the window
+ uint32_t value_list[1] = {CLIENT_WIN_EVENT_MASK};
+ if( xcb_request_check(QX11Info::connection(), \
+ xcb_change_window_attributes_checked(QX11Info::connection(), win, XCB_CW_EVENT_MASK, value_list ) ) ){
+ //Could not change event mask - did the window get deleted already?
+ free(attr);
+ qDebug() << " - Could not change event mask";
+ return false;
+ }
+
+ return true;
+}
+
+QRect LXCB::WM_Window_Geom(WId win){
+ xcb_get_geometry_cookie_t cookie = xcb_get_geometry_unchecked(QX11Info::connection(), win);
+ xcb_get_geometry_reply_t *reply = 0;
+ QRect geom;
+ reply = xcb_get_geometry_reply(QX11Info::connection(), cookie, NULL);
+ if(reply!=0){
+ geom = QRect(reply->x, reply->y, reply->width, reply->height);
+ free(reply);
+ }
+ return geom;
+}
+
+void LXCB::setupEventsForFrame(WId frame){
+ #define FRAME_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_BUTTON_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+ uint32_t value_list[1] = {FRAME_WIN_EVENT_MASK};
+ xcb_change_window_attributes(QX11Info::connection(), frame, XCB_CW_EVENT_MASK, value_list);
+}
+
+bool LXCB::setupEventsForRoot(WId root){
+ #define ROOT_WIN_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+ if(root==0){ root = QX11Info::appRootWindow(); }
+ uint32_t value_list[1] = {ROOT_WIN_EVENT_MASK};
+ xcb_generic_error_t *status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), root, XCB_CW_EVENT_MASK, value_list));
+ return (status==0);
+}
+// --------------------------------------------------
+// ICCCM Standards (older standards)
+// --------------------------------------------------
+// -- WM_NAME
+QString LXCB::WM_ICCCM_GetName(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 != xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ return ""; //error in fetching name
+ }else{
+ return QString::fromLocal8Bit(reply.name);
+ }
+}
+
+void LXCB::WM_ICCCM_SetName(WId win, QString name){
+ xcb_icccm_set_wm_name(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit());
+}
+
+// -- WM_ICON_NAME
+QString LXCB::WM_ICCCM_GetIconName(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 != xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ return ""; //error in fetching name
+ }else{
+ return QString::fromLocal8Bit(reply.name);
+ }
+}
+
+void LXCB::WM_ICCCM_SetIconName(WId win, QString name){
+ xcb_icccm_set_wm_icon_name(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit());
+}
+
+// -- WM_CLIENT_MACHINE
+QString LXCB::WM_ICCCM_GetClientMachine(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_client_machine_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 != xcb_icccm_get_wm_client_machine_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ return ""; //error in fetching name
+ }else{
+ return QString::fromLocal8Bit(reply.name);
+ }
+}
+
+void LXCB::WM_ICCCM_SetClientMachine(WId win, QString name){
+ xcb_icccm_set_wm_client_machine(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit());
+}
+
+// -- WM_CLASS
+QString LXCB::WM_ICCCM_GetClass(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class_unchecked(QX11Info::connection(), win);
+ xcb_icccm_get_wm_class_reply_t reply;
+ if(1 != xcb_icccm_get_wm_class_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ return ""; //error in fetching name
+ }else{
+ //Returns: "<instance name>::::<class name>"
+ return ( QString::fromLocal8Bit(reply.instance_name)+"::::"+QString::fromLocal8Bit(reply.class_name) );
+ }
+}
+
+void LXCB::WM_ICCCM_SetClass(WId win, QString name){
+ xcb_icccm_set_wm_class(QX11Info::connection(), win, name.length(), name.toLocal8Bit());
+}
+
+// -- WM_TRANSIENT_FOR
+WId LXCB::WM_ICCCM_GetTransientFor(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for_unchecked(QX11Info::connection(), win);
+ xcb_window_t trans;
+ if(1!= xcb_icccm_get_wm_transient_for_reply(QX11Info::connection(), cookie, &trans, NULL) ){
+ return win; //error in fetching transient window ID (or none found)
+ }else{
+ return trans;
+ }
+}
+
+void LXCB::WM_ICCCM_SetTransientFor(WId win, WId transient){
+ xcb_icccm_set_wm_transient_for(QX11Info::connection(), win, transient);
+}
+
+// -- WM_SIZE_HINTS (older property?)
+icccm_size_hints LXCB::WM_ICCCM_GetSizeHints(WId win){
+ //most values in structure are -1 if not set
+ icccm_size_hints hints;
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_size_hints_unchecked(QX11Info::connection(), win, XCB_ATOM_WM_SIZE_HINTS);
+ xcb_size_hints_t reply;
+ if(1==xcb_icccm_get_wm_size_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ //Now go though and move any data into the output struct
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_POSITION)==XCB_ICCCM_SIZE_HINT_US_POSITION ){ hints.x=reply.x; hints.y=reply.y; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_SIZE)==XCB_ICCCM_SIZE_HINT_US_SIZE ){ hints.width=reply.width; hints.height=reply.height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_POSITION)==XCB_ICCCM_SIZE_HINT_P_POSITION ){ hints.x=reply.x; hints.y=reply.y; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_SIZE)==XCB_ICCCM_SIZE_HINT_P_SIZE ){ hints.width=reply.width; hints.height=reply.height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)==XCB_ICCCM_SIZE_HINT_P_MIN_SIZE ){ hints.min_width=reply.min_width; hints.min_height=reply.min_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)==XCB_ICCCM_SIZE_HINT_P_MAX_SIZE ){ hints.max_width=reply.max_width; hints.max_height=reply.max_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)==XCB_ICCCM_SIZE_HINT_P_RESIZE_INC ){ hints.width_inc=reply.width_inc; hints.height_inc=reply.height_inc; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_ASPECT)==XCB_ICCCM_SIZE_HINT_P_ASPECT ){ hints.min_aspect_num=reply.min_aspect_num; hints.min_aspect_den=reply.min_aspect_den; hints.max_aspect_num=reply.max_aspect_num; hints.max_aspect_den=reply.max_aspect_den;}
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_BASE_SIZE)==XCB_ICCCM_SIZE_HINT_BASE_SIZE ){ hints.base_width=reply.base_width; hints.base_height=reply.base_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)==XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY ){ hints.win_gravity=reply.win_gravity; }
+ //free(reply);
+ }
+ return hints;
+}
+
+//void WM_ICCCM_SetSizeHints(WId win, icccm_size_hints hints);
+
+// -- WM_NORMAL_HINTS (newer property? - check for this before falling back on WM_SIZE_HINTS)
+icccm_size_hints LXCB::WM_ICCCM_GetNormalHints(WId win){
+//most values in structure are -1 if not set
+ //most values in structure are -1 if not set
+ icccm_size_hints hints;
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_normal_hints_unchecked(QX11Info::connection(), win);
+ xcb_size_hints_t reply;
+ if(1==xcb_icccm_get_wm_normal_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ //Now go though and move any data into the output struct
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_POSITION)==XCB_ICCCM_SIZE_HINT_US_POSITION ){ hints.x=reply.x; hints.y=reply.y; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_SIZE)==XCB_ICCCM_SIZE_HINT_US_SIZE ){ hints.width=reply.width; hints.height=reply.height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_POSITION)==XCB_ICCCM_SIZE_HINT_P_POSITION ){ hints.x=reply.x; hints.y=reply.y; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_SIZE)==XCB_ICCCM_SIZE_HINT_P_SIZE ){ hints.width=reply.width; hints.height=reply.height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)==XCB_ICCCM_SIZE_HINT_P_MIN_SIZE ){ hints.min_width=reply.min_width; hints.min_height=reply.min_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)==XCB_ICCCM_SIZE_HINT_P_MAX_SIZE ){ hints.max_width=reply.max_width; hints.max_height=reply.max_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)==XCB_ICCCM_SIZE_HINT_P_RESIZE_INC ){ hints.width_inc=reply.width_inc; hints.height_inc=reply.height_inc; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_ASPECT)==XCB_ICCCM_SIZE_HINT_P_ASPECT ){ hints.min_aspect_num=reply.min_aspect_num; hints.min_aspect_den=reply.min_aspect_den; hints.max_aspect_num=reply.max_aspect_num; hints.max_aspect_den=reply.max_aspect_den;}
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_BASE_SIZE)==XCB_ICCCM_SIZE_HINT_BASE_SIZE ){ hints.base_width=reply.base_width; hints.base_height=reply.base_height; }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)==XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY ){ hints.win_gravity=reply.win_gravity; }
+ //free(reply);
+ }
+ return hints;
+}
+
+/*void LXCB::WM_ICCCM_SetNormalHints(WId win, icccm_size_hints hints){
+ //Convert the data structure into the proper format
+ xcb_size_hints_t xhints;
+ if(hints.x>=0 || hints.y>=0){ xcb_icccm_size_hints_set_position(&xhints, 1, hints.x, hints.y); }
+ //if(hints.width>=0
+
+ xcb_icccm_set_wm_normal_hints(QX11Info::connection(), win, &xhints);
+}*/
+
+// -- WM_HINTS
+
+// -- WM_PROTOCOLS
+LXCB::ICCCM_PROTOCOLS LXCB::WM_ICCCM_GetProtocols(WId win){
+ if(atoms.isEmpty()){ createWMAtoms(); }
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_protocols(QX11Info::connection(), win, EWMH.WM_PROTOCOLS);
+ xcb_icccm_get_wm_protocols_reply_t reply;
+ LXCB::ICCCM_PROTOCOLS flags;
+ if(1==xcb_icccm_get_wm_protocols_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==ATOMS[atoms.indexOf("WM_TAKE_FOCUS")]){ flags = flags | TAKE_FOCUS; }
+ else if(reply.atoms[i]==ATOMS[atoms.indexOf("WM_DELETE_WINDOW")]){ flags = flags | DELETE_WINDOW; }
+ }
+ }
+ return flags;
+}
+
+void LXCB::WM_ICCCM_SetProtocols(WId win, LXCB::ICCCM_PROTOCOLS flags){
+ if(atoms.isEmpty()){ createWMAtoms(); }
+ xcb_atom_t *list;
+ int num;
+ if(flags.testFlag(TAKE_FOCUS) && flags.testFlag(DELETE_WINDOW)){
+ num = 2;
+ list = new xcb_atom_t[2];
+ list[0] = ATOMS[atoms.indexOf("WM_TAKE_FOCUS")];
+ list[1] = ATOMS[atoms.indexOf("WM_DELETE_WINDOW")];
+ }else if(flags.testFlag(TAKE_FOCUS)){
+ num = 1;
+ list = new xcb_atom_t[1];
+ list[0] = ATOMS[atoms.indexOf("WM_TAKE_FOCUS")];
+ }else if(flags.testFlag(DELETE_WINDOW)){
+ num = 1;
+ list = new xcb_atom_t[1];
+ list[0] = ATOMS[atoms.indexOf("WM_DELETE_WINDOW")];
+ }else{
+ num = 0;
+ list = new xcb_atom_t[0];
+ }
+ xcb_icccm_set_wm_protocols(QX11Info::connection(), win, EWMH.WM_PROTOCOLS, num, list);
+
+}
+
+// --------------------------------------------------------
+// NET_WM Standards (newer standards)
+// --------------------------------------------------------
+// _NET_SUPPORTED (Root)
+void LXCB::WM_Set_Root_Supported(){
+ //NET_WM standards (ICCCM implied - no standard way to list those)
+ xcb_atom_t list[] = {};
+ xcb_ewmh_set_supported(&EWMH, QX11Info::appScreen(), 0,list);
+}
+
+// _NET_CLIENT_LIST
+QList<WId> LXCB::WM_Get_Client_List(bool stacking){
+ QList<WId> out;
+ if(stacking){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_stacking(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_windows_reply_t reply;
+ if(1==xcb_ewmh_get_client_list_stacking_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.windows_len; i++){
+ out << reply.windows[i];
+ }
+ }
+ }else{
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_windows_reply_t reply;
+ if(1==xcb_ewmh_get_client_list_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.windows_len; i++){
+ out << reply.windows[i];
+ }
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Client_List(QList<WId> list, bool stacking){
+ //convert the QList into a generic array
+ xcb_window_t array[list.length()];
+ for(int i=0; i<list.length(); i++){ array[i] = list[i]; }
+ if(stacking){
+ xcb_ewmh_set_client_list_stacking(&EWMH, QX11Info::appScreen(), list.length(), array);
+ }else{
+ xcb_ewmh_set_client_list(&EWMH, QX11Info::appScreen(), list.length(), array);
+ }
+
+}
+
+// _NET_NUMBER_OF_DESKTOPS
+unsigned int LXCB::WM_Get_Number_Desktops(){
+ //return value equals 0 for errors
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_number_of_desktops_unchecked(&EWMH, QX11Info::appScreen());
+ uint32_t number = 0;
+ xcb_ewmh_get_number_of_desktops_reply(&EWMH, cookie, &number, NULL);
+ return number;
+}
+
+void LXCB::WM_SetNumber_Desktops(unsigned int number){
+ //NOTE: number should be at least 1
+ xcb_ewmh_set_number_of_desktops(&EWMH, QX11Info::appScreen(), number);
+}
+
+// _NET_DESKTOP_GEOMETRY
+QSize LXCB::WM_Get_Desktop_Geometry(){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_desktop_geometry(&EWMH, QX11Info::appScreen());
+ uint32_t wid, hi;
+ wid = hi = 0;
+ xcb_ewmh_get_desktop_geometry_reply(&EWMH, cookie, &wid, &hi, NULL);
+ return QSize(wid,hi);
+}
+
+void LXCB::WM_Set_Desktop_Geometry(QSize size){
+ xcb_ewmh_set_desktop_geometry(&EWMH, QX11Info::appScreen(), size.width(), size.height());
+}
+
+// _NET_DESKTOP_VIEWPORT
+QList<QPoint> LXCB::WM_Get_Desktop_Viewport(){
+ QList<QPoint> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_desktop_viewport_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_desktop_viewport_reply_t reply;
+ if(1==xcb_ewmh_get_desktop_viewport_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.desktop_viewport_len; i++){
+ out << QPoint( reply.desktop_viewport[i].x, reply.desktop_viewport[i].y );
+ }
+ xcb_ewmh_get_desktop_viewport_reply_wipe(&reply); //clean up the reply structure first
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Desktop_Viewport(QList<QPoint> list){
+ //Turn the QList into xcb_ewmh_coordinates_t*
+ xcb_ewmh_coordinates_t array[list.length()];
+ for(int i=0; i<list.length(); i++){ array[i].x=list[i].x(); array[i].y=list[i].y(); }
+ //Now set the property
+ xcb_ewmh_set_desktop_viewport(&EWMH, QX11Info::appScreen(), list.length(), array);
+}
+
+// _NET_CURRENT_DESKTOP
+int LXCB::WM_Get_Current_Desktop(){
+ //Returns -1 for errors
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop_unchecked(&EWMH, QX11Info::appScreen());
+ uint32_t num = 0;
+ if(1==xcb_ewmh_get_current_desktop_reply(&EWMH, cookie, &num, NULL) ){
+ return num;
+ }else{
+ return -1;
+ }
+}
+
+void LXCB::WM_Set_Current_Desktop(unsigned int num){
+ xcb_ewmh_set_current_desktop(&EWMH, QX11Info::appScreen(), num);
+}
+
+// _NET_DESKTOP_NAMES
+QStringList LXCB::WM_Get_Desktop_Names(){
+ QStringList out;
+ // ** ISSUES with the XCB_EWMH strings reply structure - 11/11/15 (skip for now)
+ // (Appears to be a char* instead of char** in the class definitions)
+ /*xcb_get_property_cookie_t cookie = xcb_ewmh_get_desktop_names_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_utf8_strings_reply_t reply;
+ if(1==xcb_ewmh_get_desktop_names_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.strings_len; i++){
+ out << QString::fromUtf8( QByteArray(reply.strings[i]) );
+ }
+ }*/
+ return out;
+}
+
+void LXCB::WM_Set_Desktop_Names(QStringList){// list){
+ // ** ISSUES with the XCB_EWMH strings input structure - 11/11/15 (skip for now)
+ // (Appears to be a char* instead of char** in the class definitions)
+ /*//Convert to an array of char arrays
+ char *array[ list.length() ];
+ for(int i=0; i<list.length(); i++){array[i] = list[i].toUtf8().data(); }
+ //Now set the property
+ xcb_ewmh_set_desktop_names(&EWMH, QX11Info::appScreen(), list.length(), array);
+ */
+}
+
+// _NET_ACTIVE_WINDOW
+WId LXCB::WM_Get_Active_Window(){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_window_t win = 0;
+ xcb_ewmh_get_active_window_reply(&EWMH, cookie, &win, NULL);
+ return win;
+}
+
+void LXCB::WM_Set_Active_Window(WId win){
+ xcb_ewmh_set_active_window(&EWMH, QX11Info::appScreen(), win);
+}
+
+// _NET_WORKAREA
+QList<QRect> LXCB::WM_Get_Workarea(){
+ QList<QRect> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_workarea_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_workarea_reply_t reply;
+ if(1==xcb_ewmh_get_workarea_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.workarea_len ;i++){
+ out << QRect( reply.workarea[i].x, reply.workarea[i].y, reply.workarea[i].width, reply.workarea[i].height);
+ }
+ xcb_ewmh_get_workarea_reply_wipe(&reply);
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Workarea(QList<QRect> list){
+ //Convert to the XCB/EWMH data structures
+ xcb_ewmh_geometry_t array[list.length()];
+ for(int i=0; i<list.length(); i++){
+ array[i].x = list[i].x(); array[i].y = list[i].y();
+ array[i].width = list[i].width(); array[i].height = list[i].height();
+ }
+ //Now set the property
+ xcb_ewmh_set_workarea(&EWMH, QX11Info::appScreen(), list.length(), array);
+}
+
+// _NET_SUPPORTING_WM_CHECK
+WId LXCB::WM_Get_Supporting_WM(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_supporting_wm_check_unchecked(&EWMH, win);
+ xcb_window_t out = 0;
+ xcb_ewmh_get_supporting_wm_check_reply(&EWMH, cookie, &out, NULL);
+ return win;
+}
+
+void LXCB::WM_Set_Supporting_WM(WId child){
+ //Set this property on the root window first
+ xcb_ewmh_set_supporting_wm_check(&EWMH, QX11Info::appRootWindow(), child);
+ //Also set this property on the child window (pointing to itself)
+ xcb_ewmh_set_supporting_wm_check(&EWMH, child, child);
+}
+
+// _NET_VIRTUAL_ROOTS
+QList<WId> LXCB::WM_Get_Virtual_Roots(){
+ QList<WId> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_virtual_roots_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_ewmh_get_windows_reply_t reply;
+ if(1==xcb_ewmh_get_virtual_roots_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.windows_len; i++){
+ out << reply.windows[i];
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Virtual_Roots(QList<WId> list){
+ //Convert to XCB array
+ xcb_window_t array[list.length()];
+ for(int i=0; i<list.length(); i++){ array[i] = list[i]; }
+ //Set the property
+ xcb_ewmh_set_virtual_roots(&EWMH, QX11Info::appScreen(), list.length(), array);
+}
+
+// _NET_DESKTOP_LAYOUT
+// -- skipped for now - see note in LuminaX11.h
+
+// _NET_SHOWING_DESKTOP
+bool LXCB::WM_Get_Showing_Desktop(){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_showing_desktop_unchecked(&EWMH, QX11Info::appScreen());
+ uint32_t reply = 0;
+ xcb_ewmh_get_showing_desktop_reply(&EWMH, cookie, &reply, NULL);
+ return (reply==1);
+}
+
+void LXCB::WM_Set_Showing_Desktop(bool show){
+ xcb_ewmh_set_showing_desktop(&EWMH, QX11Info::appScreen(), (show ? 1 : 0) );
+}
+
+// -- ROOT WINDOW MESSAGES/REQUESTS
+// _NET_CLOSE_WINDOW
+void LXCB::WM_Request_Close_Window(WId win){
+ xcb_ewmh_request_close_window(&EWMH, QX11Info::appScreen(), win, XCB_TIME_CURRENT_TIME, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER); //user choice to close the window
+}
+
+// _NET_MOVERESIZE_WINDOW
+void LXCB::WM_Request_MoveResize_Window(WId win, QRect geom, bool fromuser, LXCB::GRAVITY grav, LXCB::MOVERESIZE_WINDOW_FLAGS flags){
+ //Note: The LXCB::GRAVITY enum exactly matches the XCB values (just different names)
+ //Convert the flags into the XCB type
+ int eflags = 0; //xcb_ewmh_moveresize_window_opt_flags_t
+ if(flags.testFlag(LXCB::X)){ eflags = eflags | XCB_EWMH_MOVERESIZE_WINDOW_X; }
+ if(flags.testFlag(LXCB::Y)){ eflags = eflags | XCB_EWMH_MOVERESIZE_WINDOW_Y; }
+ if(flags.testFlag(LXCB::WIDTH)){ eflags = eflags | XCB_EWMH_MOVERESIZE_WINDOW_WIDTH; }
+ if(flags.testFlag(LXCB::HEIGHT)){ eflags = eflags | XCB_EWMH_MOVERESIZE_WINDOW_HEIGHT; }
+
+ xcb_ewmh_request_moveresize_window(&EWMH, QX11Info::appScreen(), win, (xcb_gravity_t) grav, \
+ (fromuser ? XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER : XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL), \
+ (xcb_ewmh_moveresize_window_opt_flags_t) eflags, geom.x(), geom.y(), geom.width(), geom.height() );
+}
+
+// _NET_WM_MOVERESIZE
+// -- skipped for now - see note in LuminaX11.h
+
+// _NET_RESTACK_WINDOW
+void LXCB::WM_Request_Restack_Window(WId win, WId sibling, LXCB::STACK_FLAG flag){
+ //Note: The STACK_FLAG enum matches the xcb_stack_mode_t enum exactly (just different names)
+ xcb_ewmh_request_restack_window(&EWMH, QX11Info::appScreen(), win, sibling, (xcb_stack_mode_t) flag);
+}
+
+// _NET_REQUEST_FRAME_EXTENTS
+void LXCB::WM_Request_Frame_Extents(WId win){
+ xcb_ewmh_request_frame_extents(&EWMH, QX11Info::appScreen(), win);
+}
+
+// === WINDOW PROPERTIES ===
+// _NET_SUPPORTED (Window)
+void LXCB::WM_Set_Window_Supported(WId win){
+ //NET_WM standards (ICCCM implied - no standard way to list those)
+ xcb_atom_t list[] = {};
+ xcb_ewmh_set_wm_allowed_actions(&EWMH, win, 0, list);
+}
+
+// _NET_WM_NAME
+QString LXCB::WM_Get_Name(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name_unchecked(&EWMH, win);
+ xcb_ewmh_get_utf8_strings_reply_t reply;
+ QString out;
+ if(1==xcb_ewmh_get_wm_name_reply(&EWMH, cookie,&reply, NULL) ){
+ out = QString::fromUtf8(reply.strings);
+ }
+ return out;
+}
+void LXCB::WM_Set_Name(WId win, QString txt){
+ xcb_ewmh_set_wm_name(&EWMH, win, txt.length(), txt.toUtf8().data());
+}
+
+// _NET_WM_VISIBLE_NAME
+QString LXCB::WM_Get_Visible_Name(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&EWMH, win);
+ xcb_ewmh_get_utf8_strings_reply_t reply;
+ QString out;
+ if(1==xcb_ewmh_get_wm_visible_name_reply(&EWMH, cookie,&reply, NULL) ){
+ out = QString::fromUtf8(reply.strings);
+ }
+ return out;
+}
+void LXCB::WM_Set_Visible_Name(WId win, QString txt){
+ xcb_ewmh_set_wm_visible_name(&EWMH, win, txt.length(), txt.toUtf8().data());
+}
+
+// _NET_WM_ICON_NAME
+QString LXCB::WM_Get_Icon_Name(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name_unchecked(&EWMH, win);
+ xcb_ewmh_get_utf8_strings_reply_t reply;
+ QString out;
+ if(1==xcb_ewmh_get_wm_icon_name_reply(&EWMH, cookie,&reply, NULL) ){
+ out = QString::fromUtf8(reply.strings);
+ }
+ return out;
+}
+void LXCB::WM_Set_Icon_Name(WId win, QString txt){
+ xcb_ewmh_set_wm_icon_name(&EWMH, win, txt.length(), txt.toUtf8().data());
+}
+
+// _NET_WM_VISIBLE_ICON_NAME
+QString LXCB::WM_Get_Visible_Icon_Name(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&EWMH, win);
+ xcb_ewmh_get_utf8_strings_reply_t reply;
+ QString out;
+ if(1==xcb_ewmh_get_wm_visible_icon_name_reply(&EWMH, cookie,&reply, NULL) ){
+ out = QString::fromUtf8(reply.strings);
+ }
+ return out;
+}
+void LXCB::WM_Set_Visible_Icon_Name(WId win, QString txt){
+ xcb_ewmh_set_wm_visible_icon_name(&EWMH, win, txt.length(), txt.toUtf8().data());
+}
+
+// _NET_WM_DESKTOP
+int LXCB::WM_Get_Desktop(WId win){
+ //returns -1 if window on all desktops
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop_unchecked(&EWMH, win);
+ uint32_t num = 0;
+ int out = -1;
+ if(1==xcb_ewmh_get_wm_desktop_reply(&EWMH, cookie, &num, NULL) ){
+ if(num!=0xFFFFFFFF){ out = num; }
+ }else{
+ //Error in fetching property (not set?)
+ // - put it on the current screen
+ out = WM_Get_Current_Desktop();
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Desktop(WId win, int num){
+ //use -1 to set it for all desktops
+ xcb_ewmh_set_wm_desktop(&EWMH, win, (num<0 ? 0xFFFFFFFF : qAbs(num) ) );
+}
+
+// _NET_WM_WINDOW_TYPE
+QList<LXCB::WINDOWTYPE> LXCB::WM_Get_Window_Type(WId win){
+ // Note: This will silently discard any unknown/non-standard window type flags
+ // The client should ensure to set at least one standardized type flag per the specifications.
+ QList<LXCB::WINDOWTYPE> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_window_type_unchecked(&EWMH, win);
+ xcb_ewmh_get_atoms_reply_t reply;
+ if(1==xcb_ewmh_get_wm_window_type_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_DESKTOP){ out << LXCB::T_DESKTOP; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_DOCK){ out << LXCB::T_DOCK; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_TOOLBAR){ out << LXCB::T_TOOLBAR; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_MENU){ out << LXCB::T_MENU; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_UTILITY){ out << LXCB::T_UTILITY; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_SPLASH){ out << LXCB::T_SPLASH; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_DIALOG){ out << LXCB::T_DIALOG; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_DROPDOWN_MENU){ out << LXCB::T_DROPDOWN_MENU; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_POPUP_MENU){ out << LXCB::T_POPUP_MENU; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_TOOLTIP){ out << LXCB::T_TOOLTIP; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_NOTIFICATION){ out << LXCB::T_NOTIFICATION; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_COMBO){ out << LXCB::T_COMBO; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_DND){ out << LXCB::T_DND; }
+ else if(reply.atoms[i]==EWMH._NET_WM_WINDOW_TYPE_NORMAL){ out << LXCB::T_NORMAL; }
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Window_Type(WId win, QList<LXCB::WINDOWTYPE> list){
+ //Convert to the XCB format
+ xcb_atom_t array[list.length()];
+ for(int i=0; i<list.length(); i++){
+ switch(list[i]){
+ case LXCB::T_DESKTOP:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_DESKTOP; break;
+ case LXCB::T_DOCK:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_DOCK; break;
+ case LXCB::T_TOOLBAR:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_TOOLBAR; break;
+ case LXCB::T_MENU:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_MENU; break;
+ case LXCB::T_UTILITY:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_UTILITY; break;
+ case LXCB::T_SPLASH:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_SPLASH; break;
+ case LXCB::T_DIALOG:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_DIALOG; break;
+ case LXCB::T_DROPDOWN_MENU:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_DROPDOWN_MENU; break;
+ case LXCB::T_POPUP_MENU:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_POPUP_MENU; break;
+ case LXCB::T_TOOLTIP:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_TOOLTIP; break;
+ case LXCB::T_NOTIFICATION:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_NOTIFICATION; break;
+ case LXCB::T_COMBO:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_COMBO; break;
+ case LXCB::T_DND:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_DND; break;
+ default:
+ array[i] = EWMH._NET_WM_WINDOW_TYPE_NORMAL;
+ }
+ }
+ //Now set the property
+ xcb_ewmh_set_wm_window_type(&EWMH, win, list.length(), array);
+}
+
+// _NET_WM_STATE
+QList<LXCB::WINDOWSTATE> LXCB::WM_Get_Window_States(WId win){
+ QList<LXCB::WINDOWSTATE> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win);
+ xcb_ewmh_get_atoms_reply_t reply;
+ if(1==xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==EWMH._NET_WM_STATE_MODAL){ out << LXCB::S_MODAL; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_STICKY){ out << LXCB::S_STICKY; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_MAXIMIZED_VERT){ out << LXCB::S_MAX_VERT; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_MAXIMIZED_HORZ){ out << LXCB::S_MAX_HORZ; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_SHADED){ out << LXCB::S_SHADED; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_SKIP_TASKBAR){ out << LXCB::S_SKIP_TASKBAR; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_SKIP_PAGER){ out << LXCB::S_SKIP_PAGER; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_HIDDEN){ out << LXCB::S_HIDDEN; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_FULLSCREEN){ out << LXCB::S_FULLSCREEN; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_ABOVE){ out << LXCB::S_ABOVE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_BELOW){ out << LXCB::S_BELOW; }
+ else if(reply.atoms[i]==EWMH._NET_WM_STATE_DEMANDS_ATTENTION){ out << LXCB::S_ATTENTION; }
+ //else if(reply.atoms[i]==EWMH._NET_WM_STATE_FOCUSED){ out << LXCB::FOCUSED; }
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Window_States(WId win, QList<LXCB::WINDOWSTATE> list){
+ //Convert to the XCB format
+ xcb_atom_t array[list.length()];
+ for(int i=0; i<list.length(); i++){
+ switch(list[i]){
+ case LXCB::S_MODAL:
+ array[i] = EWMH._NET_WM_STATE_MODAL; break;
+ case LXCB::S_STICKY:
+ array[i] = EWMH._NET_WM_STATE_STICKY; break;
+ case LXCB::S_MAX_VERT:
+ array[i] = EWMH._NET_WM_STATE_MAXIMIZED_VERT; break;
+ case LXCB::S_MAX_HORZ:
+ array[i] = EWMH._NET_WM_STATE_MAXIMIZED_HORZ; break;
+ case LXCB::S_SHADED:
+ array[i] = EWMH._NET_WM_STATE_SHADED; break;
+ case LXCB::S_SKIP_TASKBAR:
+ array[i] = EWMH._NET_WM_STATE_SKIP_TASKBAR; break;
+ case LXCB::S_SKIP_PAGER:
+ array[i] = EWMH._NET_WM_STATE_SKIP_PAGER; break;
+ case LXCB::S_HIDDEN:
+ array[i] = EWMH._NET_WM_STATE_HIDDEN; break;
+ case LXCB::S_FULLSCREEN:
+ array[i] = EWMH._NET_WM_STATE_FULLSCREEN; break;
+ case LXCB::S_ABOVE:
+ array[i] = EWMH._NET_WM_STATE_ABOVE; break;
+ case LXCB::S_BELOW:
+ array[i] = EWMH._NET_WM_STATE_BELOW; break;
+ case LXCB::S_ATTENTION:
+ array[i] = EWMH._NET_WM_STATE_DEMANDS_ATTENTION; break;
+ //case LXCB::FOCUSED:
+ //array[i] = EWMH._NET_WM_STATE_FOCUSED; break;
+ }
+ }
+ //Now set the property
+ xcb_ewmh_set_wm_state(&EWMH, win, list.length(), array);
+}
+
+// _NET_WM_ALLOWED_ACTIONS
+QList<LXCB::WINDOWACTION> LXCB::WM_Get_Window_Actions(WId win){
+ QList<LXCB::WINDOWACTION> out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_allowed_actions_unchecked(&EWMH, win);
+ xcb_ewmh_get_atoms_reply_t reply;
+ if(1==xcb_ewmh_get_wm_allowed_actions_reply(&EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==EWMH._NET_WM_ACTION_MOVE){ out << LXCB::A_MOVE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_RESIZE){ out << LXCB::A_RESIZE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_MINIMIZE){ out << LXCB::A_MINIMIZE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_SHADE){ out << LXCB::A_SHADE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_STICK){ out << LXCB::A_STICK; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_MAXIMIZE_HORZ){ out << LXCB::A_MAX_HORZ; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_MAXIMIZE_VERT){ out << LXCB::A_MAX_VERT; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_FULLSCREEN){ out << LXCB::A_FULLSCREEN; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_CHANGE_DESKTOP){ out << LXCB::A_CHANGE_DESKTOP; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_CLOSE){ out << LXCB::A_CLOSE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_ABOVE){ out << LXCB::A_ABOVE; }
+ else if(reply.atoms[i]==EWMH._NET_WM_ACTION_BELOW){ out << LXCB::A_BELOW; }
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Window_Actions(WId win, QList<LXCB::WINDOWACTION> list){
+ //Convert to the XCB format
+ xcb_atom_t array[list.length()];
+ for(int i=0; i<list.length(); i++){
+ switch(list[i]){
+ case LXCB::A_MOVE:
+ array[i] = EWMH._NET_WM_ACTION_MOVE; break;
+ case LXCB::A_RESIZE:
+ array[i] = EWMH._NET_WM_ACTION_RESIZE; break;
+ case LXCB::A_MINIMIZE:
+ array[i] = EWMH._NET_WM_ACTION_MINIMIZE; break;
+ case LXCB::A_SHADE:
+ array[i] = EWMH._NET_WM_ACTION_SHADE; break;
+ case LXCB::A_STICK:
+ array[i] = EWMH._NET_WM_ACTION_STICK; break;
+ case LXCB::A_MAX_HORZ:
+ array[i] = EWMH._NET_WM_ACTION_MAXIMIZE_HORZ; break;
+ case LXCB::A_MAX_VERT:
+ array[i] = EWMH._NET_WM_ACTION_MAXIMIZE_VERT; break;
+ case LXCB::A_FULLSCREEN:
+ array[i] = EWMH._NET_WM_ACTION_FULLSCREEN; break;
+ case LXCB::A_CHANGE_DESKTOP:
+ array[i] = EWMH._NET_WM_ACTION_CHANGE_DESKTOP; break;
+ case LXCB::A_CLOSE:
+ array[i] = EWMH._NET_WM_ACTION_CLOSE; break;
+ case LXCB::A_ABOVE:
+ array[i] = EWMH._NET_WM_ACTION_ABOVE; break;
+ case LXCB::A_BELOW:
+ array[i] = EWMH._NET_WM_ACTION_BELOW; break;
+ }
+ }
+ //Now set the property
+ xcb_ewmh_set_wm_allowed_actions(&EWMH, win, list.length(), array);
+}
+
+// _NET_WM_STRUT
+QList<unsigned int> LXCB::WM_Get_Window_Strut(WId win){
+ //Returns: [left,right,top,bottom] margins in pixels (always length 4)
+ QList<unsigned int> out; out << 0 << 0 << 0 << 0; //init the output list
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_strut_unchecked(&EWMH, win);
+ xcb_ewmh_get_extents_reply_t reply;
+ if(1==xcb_ewmh_get_wm_strut_reply(&EWMH, cookie, &reply, NULL) ){
+ out[0] = reply.left;
+ out[1] = reply.right;
+ out[2] = reply.top;
+ out[3] = reply.bottom;
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Window_Strut(WId win, QList<unsigned int> margins){
+ //Input: [left, right, top, bottom] - must be length 4
+ while(margins.length()<4){ margins << 0; }
+ xcb_ewmh_set_wm_strut(&EWMH, win, margins[0], margins[1], margins[2], margins[3]);
+}
+
+// _NET_WM_STRUT_PARTIAL
+QList<strut_geom> LXCB::WM_Get_Window_Strut_Partial(WId win){
+ //Returns: [left,right,top,bottom] struts
+ QList<strut_geom> out; out << strut_geom() << strut_geom() << strut_geom() << strut_geom();
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_strut_partial_unchecked(&EWMH, win);
+ xcb_ewmh_wm_strut_partial_t reply;
+ if(1==xcb_ewmh_get_wm_strut_partial_reply(&EWMH, cookie, &reply, NULL) ){
+ if(reply.left>0){
+ out[0].start = reply.left_start_y; out[0].end = reply.left_end_y; out[0].thick = reply.left;
+ }
+ if(reply.right>0){
+ out[1].start = reply.right_start_y; out[1].end = reply.right_end_y; out[1].thick = reply.right;
+ }
+ if(reply.top>0){
+ out[2].start = reply.top_start_x; out[2].end = reply.top_end_x; out[2].thick = reply.top;
+ }
+ if(reply.bottom>0){
+ out[3].start = reply.bottom_start_x; out[3].end = reply.bottom_end_x; out[3].thick = reply.bottom;
+ }
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Window_Strut_Partial(WId win, QList<strut_geom> struts){
+ //Input: [left,right,top,bottom] - must be length 4
+ while(struts.length() < 4){ struts << strut_geom(); }
+ //Convert to the XCB input format
+ xcb_ewmh_wm_strut_partial_t input;
+ input.left=struts[0].thick; input.left_start_y=struts[0].start; input.left_end_y=struts[0].end;
+ input.right=struts[1].thick; input.right_start_y=struts[1].start; input.right_end_y=struts[1].end;
+ input.top=struts[2].thick; input.top_start_x=struts[2].start; input.top_end_x=struts[2].end;
+ input.bottom=struts[3].thick; input.bottom_start_x=struts[3].start; input.bottom_end_x=struts[3].end;
+ //Now set the property
+ xcb_ewmh_set_wm_strut_partial(&EWMH, win, input);
+}
+
+// _NET_WM_ICON_GEOMETRY
+QRect LXCB::WM_Get_Icon_Geometry(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_geometry_unchecked(&EWMH, win);
+ xcb_ewmh_geometry_t reply;
+ QRect out;
+ if(1==xcb_ewmh_get_wm_icon_geometry_reply(&EWMH, cookie, &reply, NULL) ){
+ out = QRect(reply.x, reply.y, reply.width, reply.height);
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Icon_Geometry(WId win, QRect geom){
+ //Note - 11/12/15: xcb_ewmh.h lists the inputs as "left/right/top/bottom"
+ // but this might be an error and the real inputs are "x/y/width/height"
+ // as in the other geometry get/set routines (and as returned by the xcb_ewmh_get_wm_icon_geometry() routine)
+ xcb_ewmh_set_wm_icon_geometry(&EWMH, win, geom.x(), geom.x()+geom.width(), geom.y(), geom.y()+geom.height());
+ //xcb_ewmh_set_wm_icon_geometry(&EWMH, win, geom.x(), geom.y(), geom.width(), geom.height());
+}
+
+// _NET_WM_ICON
+QIcon LXCB::WM_Get_Icon(WId win){
+ //Note: The output is a QIcon because it allows for multiple varying-sized images to be loaded/used later as needed
+ // For each pixmap found here, add it (in its native size) to the icon structure
+ QIcon out;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&EWMH, win);
+ xcb_ewmh_get_wm_icon_reply_t reply;
+ if(1==xcb_ewmh_get_wm_icon_reply(&EWMH, cookie, &reply, NULL) ){
+ //Now iterate over all the pixmaps and load them into the QIcon
+ xcb_ewmh_wm_icon_iterator_t it = xcb_ewmh_get_wm_icon_iterator(&reply);
+ while(it.index < reply.num_icons){
+ QImage img( (const unsigned char *) it.data, it.width, it.height, QImage::Format_ARGB32);
+ out.addPixmap( QPixmap::fromImage(img) );
+ if(it.rem>0){ xcb_ewmh_get_wm_icon_next(&it); } //go to the next pixmap
+ else{ break; } //just finished the last one - ensure this breaks out now (just in case)
+ }
+ //Clean up any background buffer for the reply
+ xcb_ewmh_get_wm_icon_reply_wipe(&reply);
+ }
+ return out;
+}
+
+// _NET_WM_PID
+unsigned int LXCB::WM_Get_Pid(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_pid_unchecked(&EWMH, win);
+ uint32_t pid = 0;
+ xcb_ewmh_get_wm_pid_reply(&EWMH, cookie, &pid, NULL);
+ return pid;
+}
+
+// _NET_WM_HANDLED_ICONS
+bool LXCB::WM_Get_Handled_Icons(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_handled_icons_unchecked(&EWMH, win);
+ uint32_t num = 0;
+ xcb_ewmh_get_wm_handled_icons_reply(&EWMH, cookie, &num, NULL);
+ return (num!=0); //This flag is set on the window
+}
+
+void LXCB::WM_Set_Handled_Icons(WId win, bool set){
+ xcb_ewmh_set_wm_handled_icons(&EWMH, win, (set ? 1 : 0));
+}
+
+// _NET_WM_USER_TIME
+unsigned int LXCB::WM_Get_User_Time(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_user_time_unchecked(&EWMH, win);
+ uint32_t out = 0;
+ xcb_ewmh_get_wm_user_time_reply(&EWMH, cookie, &out, NULL);
+ return out;
+}
+
+void LXCB::WM_Set_User_Time(WId win, unsigned int xtime){
+ xcb_ewmh_set_wm_user_time(&EWMH, win, xtime);
+}
+
+// _NET_WM_USER_TIME_WINDOW
+/*
+WId LXCB::WM_Get_User_Time_WIndow(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_user_time_window_unchecked(&EWMH, win);
+ xcb_window_t out;
+ xcb_ewmh_get_user_time_window_reply(&EWMH, cookie, &out, NULL);
+ return out;
+}
+
+void LXCB::WM_Set_User_Time_Window(WId win, WId utwin){
+ xcb_ewmh_set_wm_user_time_window(&EWMH, win, utwin);
+}*/
+
+// _NET_FRAME_EXTENTS
+QList<unsigned int> LXCB::WM_Get_Frame_Extents(WId win){
+ //Returns: [left,right,top,bottom] margins in pixels (always length 4)
+ QList<unsigned int> out; out << 0 << 0 << 0 << 0; //init the output list
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win);
+ xcb_ewmh_get_extents_reply_t reply;
+ if(1==xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &reply, NULL) ){
+ out[0] = reply.left;
+ out[1] = reply.right;
+ out[2] = reply.top;
+ out[3] = reply.bottom;
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Frame_Extents(WId win, QList<unsigned int> margins){
+ //Input: [left, right, top, bottom] - must be length 4
+ while(margins.length()<4){ margins << 0; }
+ xcb_ewmh_set_frame_extents(&EWMH, win, margins[0], margins[1], margins[2], margins[3]);
+}
+
+// _NET_WM_OPAQUE_REGION
+
+// _NET_WM_BYPASS_COMPOSITOR
+
+// === SPECIAL WM PROTOCOLS (EWMH) ===
+// _NET_WM_PING
+void LXCB::WM_Send_Ping(WId win){
+ xcb_ewmh_send_wm_ping(&EWMH, win, XCB_TIME_CURRENT_TIME);
+}
+
+// _NET_WM_SYNC_REQUEST
+uint64_t LXCB::WM_Get_Sync_Request_Counter(WId win){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_sync_request_counter_unchecked(&EWMH, win);
+ uint64_t count = 0;
+ xcb_ewmh_get_wm_sync_request_counter_reply(&EWMH, cookie, &count, NULL);
+ return count;
+}
+
+/*void LXCB::WM_Set_Sync_Request_Counter(WId win, uint64_t count){
+
+}*/
+
+// _NET_WM_FULLSCREEN_MONITORS
+QList<unsigned int> LXCB::WM_Get_Fullscreen_Monitors(WId win){
+ //Returns: [top,bottom,left,right] monitor numbers for window to use when fullscreen
+ QList<unsigned int> out; out << 0 << 0 << 0 << 0; //init the output array
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_fullscreen_monitors_unchecked(&EWMH, win);
+ xcb_ewmh_get_wm_fullscreen_monitors_reply_t reply;
+ if(1==xcb_ewmh_get_wm_fullscreen_monitors_reply(&EWMH, cookie, &reply, NULL) ){
+ out[0] = reply.top; out[1] = reply.bottom;
+ out[2] = reply.left; out[3] = reply.right;
+ }
+ return out;
+}
+
+void LXCB::WM_Set_Fullscreen_Montors(WId win, QList<unsigned int> list){
+ //Input: [top,bottom,left,right] monitor numbers
+ while(list.length()<4){ list << 0; }
+ xcb_ewmh_set_wm_fullscreen_monitors(&EWMH, win, list[0], list[1], list[2], list[3]);
+}
+
+// _NET_WM_CM_S(n)
+WId LXCB::WM_Get_CM_Owner(){
+ xcb_get_selection_owner_cookie_t cookie = xcb_ewmh_get_wm_cm_owner_unchecked(&EWMH, QX11Info::appScreen());
+ xcb_window_t owner = 0;
+ xcb_ewmh_get_wm_cm_owner_reply(&EWMH, cookie, &owner, NULL);
+ return owner;
+}
+
+void LXCB::WM_Set_CM_Owner(WId win){
+ xcb_ewmh_set_wm_cm_owner(&EWMH, QX11Info::appScreen(), win, XCB_TIME_CURRENT_TIME,0,0);
+}
diff --git a/src-qt5/core/libLumina/LuminaX11.h b/src-qt5/core/libLumina/LuminaX11.h
new file mode 100644
index 00000000..b0a5d588
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaX11.h
@@ -0,0 +1,407 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2014-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This class governs all the XLib usage and interactions
+// and provides simpler Qt-based functions for use elsewhere
+//===========================================
+#ifndef _LUMINA_LIBRARY_X11_H
+#define _LUMINA_LIBRARY_X11_H
+
+//Qt includes
+#include <QList>
+#include <QString>
+#include <QPixmap>
+#include <QImage>
+#include <QIcon>
+#include <QPixmap>
+#include <QX11Info>
+#include <QDebug>
+#include <QPainter>
+#include <QObject>
+#include <QFlags>
+
+
+#include <xcb/xcb_ewmh.h>
+
+//SYSTEM TRAY STANDARD DEFINITIONS
+#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+#define URGENCYHINT (1L << 8) //For window urgency detection
+
+//Simple data container for the ICCCM hints (_size, _normal, _hints)
+
+//Simple data container for doing STRUT_PARTIAL input/output calculations
+class strut_geom{
+public:
+ //Note: "Thick" will always be in the direction perpenticular to the start/end coordinates
+ //Example: A left strut will use start/end as Y coordinates, with "thick" in X coordinates starting from the left edge
+ unsigned int start, end, thick;
+ strut_geom(){ start = end = thick = 0; }
+ ~strut_geom(){}
+};
+
+class icccm_size_hints{
+public:
+ int x, y, width, height, min_width, min_height, max_width, max_height;
+ //Note: The "x","y","width", and "height" values are considered depreciated in the ICCCM specs
+ int width_inc, height_inc, min_aspect_num, min_aspect_den, max_aspect_num, max_aspect_den;
+ int base_width, base_height;
+ unsigned int win_gravity; //LXCB::GRAVITY value
+
+ icccm_size_hints(){
+ x=y=width=height=min_width=max_width=min_height=max_height = -1;
+ width_inc=height_inc=min_aspect_num=min_aspect_den=max_aspect_num=max_aspect_den = -1;
+ win_gravity = 0;
+ }
+ ~icccm_size_hints(){}
+ bool isValid(){
+ //See if any of the values are different from the init values
+ return ( x>=0 || y>=0 || width>=0 || height>=0 || min_width>=0 || min_height>=0 || max_width>=0 || max_height>=0 \
+ || width_inc>=0 || height_inc>=0 || min_aspect_num>=0 || min_aspect_den>=0 || max_aspect_num>=0 || max_aspect_den>=0 \
+ || base_width>=0 || base_height>=0 || win_gravity>0 );
+ }
+};
+
+//XCB Library replacement for LX11 (Qt5 uses XCB instead of XLib)
+class LXCB{
+
+public:
+ enum WINDOWVISIBILITY {IGNORE, INVISIBLE, VISIBLE, ACTIVE, ATTENTION}; //note that this in order of priority
+ enum ICCCM_STATE {WITHDRAWN, NORMAL, ICONIC};
+ enum GRAVITY {FORGET=0, NW=1, N=2, NE=3, W=4, CENTER=5, E=6, SW=7, S=8, SE=9, STATIC=10};
+ enum STACK_FLAG {ABOVE=0, BELOW=1, TOP_IF=2, BOTTOM_IF=3, OPPOSITE=4};
+ enum WINDOWTYPE {T_DESKTOP, T_DOCK, T_TOOLBAR, T_MENU, T_UTILITY, T_SPLASH, T_DIALOG, T_DROPDOWN_MENU, T_POPUP_MENU, T_TOOLTIP, T_NOTIFICATION, T_COMBO, T_DND, T_NORMAL};
+ enum WINDOWSTATE {S_MODAL, S_STICKY, S_MAX_VERT, S_MAX_HORZ, S_SHADED, S_SKIP_TASKBAR, S_SKIP_PAGER, S_HIDDEN, S_FULLSCREEN, S_ABOVE, S_BELOW, S_ATTENTION};
+ enum WINDOWACTION {A_MOVE, A_RESIZE, A_MINIMIZE, A_SHADE, A_STICK, A_MAX_VERT, A_MAX_HORZ, A_FULLSCREEN, A_CHANGE_DESKTOP, A_CLOSE, A_ABOVE, A_BELOW};
+ //Now enums which can have multiple values at once (Use the plural form for the QFlags)
+ enum ICCCM_PROTOCOL {TAKE_FOCUS = 0x0, DELETE_WINDOW = 0x1}; //any combination
+ Q_DECLARE_FLAGS(ICCCM_PROTOCOLS, ICCCM_PROTOCOL);
+ enum SIZE_HINT { US_POSITION=1<<0, US_SIZE=1<<1, P_POSITION=1<<2, P_SIZE=1<<3, P_MIN_SIZE=1<<4, P_MAX_SIZE=1<<5, P_RESIZE_INC=1<<6, P_ASPECT=1<<7, BASE_SIZE=1<<8, P_WIN_GRAVITY=1<<9 };
+ Q_DECLARE_FLAGS(SIZE_HINTS, SIZE_HINT);
+ enum MOVERESIZE_WINDOW_FLAG { X=0x0, Y=0x1, WIDTH=0x2, HEIGHT=0x3};
+ Q_DECLARE_FLAGS(MOVERESIZE_WINDOW_FLAGS, MOVERESIZE_WINDOW_FLAG);
+
+ xcb_ewmh_connection_t EWMH; //This is where all the screen info and atoms are located
+
+ LXCB();
+ ~LXCB();
+
+ //== Main Interface functions ==
+ // General Information
+ QList<WId> WindowList(bool rawlist = false); //list all non-Lumina windows (rawlist -> all workspaces)
+ unsigned int CurrentWorkspace();
+ unsigned int NumberOfWorkspaces();
+ WId ActiveWindow(); //fetch the ID for the currently active window
+
+ //Session Modification
+ bool CheckDisableXinerama(); //returns true if Xinerama was initially set but now disabled
+ void RegisterVirtualRoots(QList<WId> roots);
+ void SetCurrentWorkspace(int);
+
+ //Window Information
+ QString WindowClass(WId);
+ unsigned int WindowWorkspace(WId); //The workspace the window is on
+ QRect WindowGeometry(WId win, bool includeFrame = true); //the geometry of the window (frame excluded)
+ QList<int> WindowFrameGeometry(WId win); //Returns: [top,bottom,left,right] sizes of the frame
+ LXCB::WINDOWVISIBILITY WindowState(WId win); //Visible state of window
+ QString WindowVisibleIconName(WId win); //_NET_WM_VISIBLE_ICON_NAME
+ QString WindowIconName(WId win); //_NET_WM_ICON_NAME
+ QString WindowVisibleName(WId win); //_NET_WM_VISIBLE_NAME
+ QString WindowName(WId win); //_NET_WM_NAME
+ QString OldWindowName(WId win); //WM_NAME (old standard)
+ QString OldWindowIconName(WId win); //WM_ICON_NAME (old standard)
+ bool WindowIsMaximized(WId win);
+ int WindowIsFullscreen(WId win); //Returns the screen number if the window is fullscreen (or -1)
+ QIcon WindowIcon(WId win); //_NET_WM_ICON
+
+ //Window Modification
+ // - SubStructure simplifications (not commonly used)
+ void SelectInput(WId win, bool isEmbed = false); //XSelectInput replacement (to see window events)
+ uint GenerateDamageID(WId);
+
+ // - General Window Modifications
+ void SetAsSticky(WId); //Stick to all workspaces
+ void SetDisableWMActions(WId); //Disable WM control (shortcuts/automatic functions)
+ void SetAsPanel(WId); //Adjust all the window flags for a proper panel (cannot be done through Qt)
+ void SetAsDesktop(WId); //Adjust window flags to set as the desktop
+ void CloseWindow(WId); //request that the window be closed
+ void KillClient(WId); //Force the application that created the window to close
+ void MinimizeWindow(WId); //request that the window be unmapped/minimized
+ void ActivateWindow(WId); //request that the window become active
+ void RestoreWindow(WId); //Re-map/raise the window
+ void MaximizeWindow(WId win, bool flagsonly = false); //request that the window become maximized
+ void MoveResizeWindow(WId win, QRect geom);
+ void ResizeWindow(WId win, int width, int height);
+ void ResizeWindow(WId win, QSize sz){ ResizeWindow(win, sz.width(), sz.height()); } //overload for simplicity
+ void ReserveLocation(WId win, QRect geom, QString loc);
+
+ //Window Embedding/Detaching (for system tray)
+ //void SetWindowBackground(QWidget *parent, QRect area, WId client);
+ uint EmbedWindow(WId win, WId container); //returns the damage ID (or 0 for an error)
+ bool UnembedWindow(WId win);
+ QPixmap TrayImage(WId win);
+
+ //System Tray Management
+ WId startSystemTray(int screen = 0); //Startup the system tray (returns window ID for tray)
+ void closeSystemTray(WId); //Close the system tray
+
+
+ //============
+ // WM Functions (directly changing/reading properties)
+ // - Using these directly may prevent the WM from seeing the change
+ //============
+ void WM_CloseWindow(WId win, bool force = false);
+ void WM_ShowWindow(WId win);
+ void WM_HideWindow(WId win);
+
+ WId WM_CreateWindow(WId parent = 0);
+
+ // WM Utility Functions
+ QList<WId> WM_RootWindows(); //return all windows which have root as the parent
+ bool WM_ManageWindow(WId win, bool needsmap = true); //return whether the window is/should be managed
+ QRect WM_Window_Geom(WId win); //Return the current window geometry
+ void setupEventsForFrame(WId frame);
+ bool setupEventsForRoot(WId root = 0);
+
+ // ICCCM Standards (older standards)
+ // -- WM_NAME
+ QString WM_ICCCM_GetName(WId win);
+ void WM_ICCCM_SetName(WId win, QString name);
+ // -- WM_ICON_NAME
+ QString WM_ICCCM_GetIconName(WId win);
+ void WM_ICCCM_SetIconName(WId win, QString name);
+ // --- WM_CLIENT_MACHINE
+ QString WM_ICCCM_GetClientMachine(WId win);
+ void WM_ICCCM_SetClientMachine(WId win, QString name);
+ // -- WM_CLASS
+ QString WM_ICCCM_GetClass(WId win); //Returns: "<instance name>::::<class name>"
+ void WM_ICCCM_SetClass(WId win, QString name);
+ // -- WM_TRANSIENT_FOR
+ WId WM_ICCCM_GetTransientFor(WId win); //Returns "win" for errors or no transient
+ void WM_ICCCM_SetTransientFor(WId win, WId transient);
+ // -- WM_SIZE_HINTS (older property?)
+ icccm_size_hints WM_ICCCM_GetSizeHints(WId win); //most values in structure are -1 if not set
+ //void WM_ICCCM_SetSizeHints(WId win, icccm_size_hints hints);
+ // -- WM_NORMAL_HINTS (newer property? - check for this before falling back on WM_SIZE_HINTS)
+ icccm_size_hints WM_ICCCM_GetNormalHints(WId win); //most values in structure are -1 if not set
+ //void WM_ICCCM_SetNormalHints(WId win, icccm_size_hints hints);
+ // -- WM_HINTS (contains WM_STATE)
+
+ // -- WM_PROTOCOLS
+ ICCCM_PROTOCOLS WM_ICCCM_GetProtocols(WId win);
+ void WM_ICCCM_SetProtocols(WId win, ICCCM_PROTOCOLS flags);
+
+ //NET_WM Standards (newer standards)
+
+ // -- ROOT WINDOW PROPERTIES
+ // _NET_SUPPORTED
+ void WM_Set_Root_Supported(); //set the atom list of supported features on the root window
+ // _NET_CLIENT_LIST
+ // Note: client list ordered oldest->newest, stacking list ordered bottom->top
+ QList<WId> WM_Get_Client_List(bool stacking = false);
+ void WM_Set_Client_List(QList<WId> list, bool stacking=false);
+
+ // _NET_NUMBER_OF_DESKTOPS
+ // Note: This is the number of virtual workspaces, not monitors
+ unsigned int WM_Get_Number_Desktops(); //return value equals 0 for errors
+ void WM_SetNumber_Desktops(unsigned int number); //should be at least 1
+
+ // _NET_DESKTOP_GEOMETRY
+ // Note: This property is the combined size and/or bounding rectangle of all monitors
+ // The definition works well for single-monitors, but gets really fuzzy for multiple monitors
+ QSize WM_Get_Desktop_Geometry();
+ void WM_Set_Desktop_Geometry(QSize);
+
+ // _NET_DESKTOP_VIEWPORT
+ // Note: This is the X/Y origin of the viewport for each monitor
+ // Thi is normally (0,0) , unless desktop larger than monitor supports
+ QList<QPoint> WM_Get_Desktop_Viewport();
+ void WM_Set_Desktop_Viewport(QList<QPoint> list);
+
+ // _NET_CURRENT_DESKTOP
+ // Note: Current workspace number. Range = 0 to (_NET_NUMBER_OF_DESKTOPS - 1)
+ int WM_Get_Current_Desktop(); //Returns -1 for errors
+ void WM_Set_Current_Desktop(unsigned int num);
+
+ // _NET_DESKTOP_NAMES
+ QStringList WM_Get_Desktop_Names();
+ void WM_Set_Desktop_Names(QStringList list);
+
+ // _NET_ACTIVE_WINDOW
+ WId WM_Get_Active_Window();
+ void WM_Set_Active_Window(WId win);
+
+ // _NET_WORKAREA
+ // Note: The workarea is the recangle for each monitor where no space is reserved
+ // This accounts for any STRUT's that are set, within the current VIEWPORT
+ QList<QRect> WM_Get_Workarea();
+ void WM_Set_Workarea(QList<QRect> list);
+
+ // _NET_SUPPORTING_WM_CHECK
+ // Note: This needs to be set on two windows: root -> child, and child->child
+ // So the "set" function will do both at the same time
+ // The child window also needs the _NET_WM_NAME set to the window manager name
+ WId WM_Get_Supporting_WM(WId win);
+ void WM_Set_Supporting_WM(WId child);
+
+ // _NET_VIRTUAL_ROOTS
+ QList<WId> WM_Get_Virtual_Roots();
+ void WM_Set_Virtual_Roots(QList<WId> list);
+
+ // _NET_DESKTOP_LAYOUT
+ // NOTE: Skip this implementation for now - is supposed to be set by a pager (not the WM)
+ // and the WM can choose to use/ignore it as necessary.
+ // (Just use the current XRandR layout instead of this setting/property)
+
+ // _NET_SHOWING_DESKTOP
+ // Note: This is true/false depending on whether the WM is hiding all windows to show the desktop only
+ bool WM_Get_Showing_Desktop();
+ void WM_Set_Showing_Desktop(bool show);
+
+ // -- ROOT WINDOW MESSAGES/REQUESTS (for non-WM usage)
+ // _NET_CLOSE_WINDOW
+ void WM_Request_Close_Window(WId win);
+
+ // _NET_MOVERESIZE_WINDOW
+ // Note: Used for finalized movement/resize operations
+ void WM_Request_MoveResize_Window(WId win, QRect geom, bool fromuser = false, LXCB::GRAVITY grav = LXCB::STATIC, LXCB::MOVERESIZE_WINDOW_FLAGS flags = LXCB::MOVERESIZE_WINDOW_FLAGS(LXCB::X | LXCB::Y | LXCB::WIDTH | LXCB::HEIGHT) );
+
+ // _NET_WM_MOVERESIZE
+ // Note: Used for interactive clicks/changes to a window size/position
+ // There are known race conditions/issues with this X format - so skip it for now (11/12/15)
+
+ // _NET_RESTACK_WINDOW
+ // Note: Send a request to re-stack a window (win) with respect to another window (sibling)
+ // based on the flag to determine how the stack order should be changed
+ void WM_Request_Restack_Window(WId win, WId sibling, LXCB::STACK_FLAG flag);
+
+ // _NET_REQUEST_FRAME_EXTENTS
+ // Note: This is used by client windows to get the _NET_FRAME_EXTENTS property pre-set
+ // by the WM before the window is actually mapped (just an estimate of the frame at least)
+ void WM_Request_Frame_Extents(WId win);
+
+ // -- WINDOW PROPERTIES
+ // _NET_SUPPORTED
+ void WM_Set_Window_Supported(WId win); //set the atom list of supported features on the given window
+
+ // _NET_WM_NAME
+ QString WM_Get_Name(WId win);
+ void WM_Set_Name(WId win, QString txt);
+
+ // _NET_WM_VISIBLE_NAME
+ QString WM_Get_Visible_Name(WId win);
+ void WM_Set_Visible_Name(WId win, QString txt);
+
+ // _NET_WM_ICON_NAME
+ QString WM_Get_Icon_Name(WId win);
+ void WM_Set_Icon_Name(WId win, QString txt);
+
+ // _NET_WM_VISIBLE_ICON_NAME
+ QString WM_Get_Visible_Icon_Name(WId win);
+ void WM_Set_Visible_Icon_Name(WId win, QString txt);
+
+ // _NET_WM_DESKTOP
+ // Note: This refers to the virtual workspace, not the monitor/screen number
+ int WM_Get_Desktop(WId win); //returns -1 if window on all desktops
+ void WM_Set_Desktop(WId win, int num); //use -1 to set it for all desktops
+
+ // _NET_WM_WINDOW_TYPE
+ // Note: While this returns a list, they are ordered by priority for WM usage (use the first one known about)
+ QList<LXCB::WINDOWTYPE> WM_Get_Window_Type(WId win);
+ void WM_Set_Window_Type(WId win, QList<LXCB::WINDOWTYPE> list);
+
+ // _NET_WM_STATE
+ QList<LXCB::WINDOWSTATE> WM_Get_Window_States(WId win);
+ void WM_Set_Window_States(WId win, QList<LXCB::WINDOWSTATE> list);
+
+ // _NET_WM_ALLOWED_ACTIONS
+ QList<LXCB::WINDOWACTION> WM_Get_Window_Actions(WId win);
+ void WM_Set_Window_Actions(WId win, QList<LXCB::WINDOWACTION> list);
+
+ // _NET_WM_STRUT
+ QList<unsigned int> WM_Get_Window_Strut(WId win); //Returns: [left,right,top,bottom] margins in pixels (always length 4)
+ void WM_Set_Window_Strut(WId win, QList<unsigned int> margins); //Input: [left, right, top, bottom] - must be length 4
+
+ // _NET_WM_STRUT_PARTIAL
+ QList<strut_geom> WM_Get_Window_Strut_Partial(WId win); //Returns: [left,right,top,bottom] struts
+ void WM_Set_Window_Strut_Partial(WId win, QList<strut_geom> struts); //Input: [left,right,top,bottom] - must be length 4
+
+ // _NET_WM_ICON_GEOMETRY
+ QRect WM_Get_Icon_Geometry(WId win);
+ void WM_Set_Icon_Geometry(WId win, QRect geom);
+
+ // _NET_WM_ICON
+ // Note: Don't write a "Set" routine for this - is handled on the client side and not the WM/DE side
+ QIcon WM_Get_Icon(WId win);
+
+ // _NET_WM_PID
+ // Note: Don't write a "Set" routine for this - is handled on the client side and not the WM/DE side
+ unsigned int WM_Get_Pid(WId win);
+
+ // _NET_WM_HANDLED_ICONS
+ // Note: Probably not going to need this - is used by pagers exclusively to tell the WM
+ // not to provide task manager icons (not needed for an integrated WM/DE combination)
+ bool WM_Get_Handled_Icons(WId win);
+ void WM_Set_Handled_Icons(WId win, bool set);
+
+ // _NET_WM_USER_TIME
+ // Note: The user time property on a client window is supposed to be updated on user activity,
+ // allowing the WM to be able to distinguish user activity from automated window actions
+ unsigned int WM_Get_User_Time(WId win);
+ void WM_Set_User_Time(WId win, unsigned int xtime);
+
+ // _NET_WM_USER_TIME_WINDOW
+ // This returns the window to watch for time update events,
+ // instead of constantly polling all the (toplevel?) windows for the app
+ // IGNORED - xcb_ewmh library does not appear to have valid support for this property yet (11/13/15)
+ //WId WM_Get_User_Time_WIndow(WId win);
+ //void WM_Set_User_Time_Window(WId win, WId utwin);
+
+ // _NET_FRAME_EXTENTS
+ QList<unsigned int> WM_Get_Frame_Extents(WId win); //Returns: [left,right,top,bottom] margins in pixels (always length 4)
+ void WM_Set_Frame_Extents(WId win, QList<unsigned int> margins); //Input: [left, right, top, bottom] - must be length 4
+
+ // _NET_WM_OPAQUE_REGION
+ // NOT SUPPORTED - missing in xcb_ewmh library (11/13/15)
+
+ // _NET_WM_BYPASS_COMPOSITOR
+ // NOT SUPPORTED - missing in xcb_ewmh library (11/13/15)
+
+ // === SPECIAL WM PROTOCOLS (EWMH) ===
+ // _NET_WM_PING
+ // Note: Used to determine if a window/app is hung before killing the process (with PID)
+ // The client window should respond instantly if it is still active (waiting on user input for instance)
+ void WM_Send_Ping(WId win);
+
+ // _NET_WM_SYNC_REQUEST
+ uint64_t WM_Get_Sync_Request_Counter(WId win);
+ //void WM_Set_Sync_Request_Counter(WId win, uint64_t count);
+
+ // _NET_WM_FULLSCREEN_MONITORS
+ QList<unsigned int> WM_Get_Fullscreen_Monitors(WId win); //Returns: [top,bottom,left,right] monitor numbers for window to use when fullscreen
+ void WM_Set_Fullscreen_Montors(WId win, QList<unsigned int> list); //Input: [top,bottom,left,right] monitor numbers
+
+ // _NET_WM_CM_S(n)
+ // Note: This is used to check/register the compositing manager for the current X screen (#n)
+ WId WM_Get_CM_Owner();
+ void WM_Set_CM_Owner(WId win);
+
+private:
+ QList<xcb_atom_t> ATOMS;
+ QStringList atoms;
+
+ void createWMAtoms(); //fill the private lists above
+};
+//Now also declare the flags for Qt to be able to use normal operations on them
+Q_DECLARE_OPERATORS_FOR_FLAGS(LXCB::ICCCM_PROTOCOLS);
+Q_DECLARE_OPERATORS_FOR_FLAGS(LXCB::MOVERESIZE_WINDOW_FLAGS);
+Q_DECLARE_OPERATORS_FOR_FLAGS(LXCB::SIZE_HINTS);
+
+#endif
diff --git a/src-qt5/core/libLumina/LuminaXDG.cpp b/src-qt5/core/libLumina/LuminaXDG.cpp
new file mode 100644
index 00000000..c3c89d37
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaXDG.cpp
@@ -0,0 +1,1157 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2013-2015, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LuminaXDG.h"
+#include "LuminaOS.h"
+#include "LuminaUtils.h"
+#include <QObject>
+#include <QMediaPlayer>
+#include <QSvgRenderer>
+
+static QStringList mimeglobs;
+static qint64 mimechecktime;
+
+//==== LFileInfo Functions ====
+//Need some extra information not usually available by a QFileInfo
+void LFileInfo::loadExtraInfo(){
+ //Now load the extra information
+ if(this->isDir()){
+ mime = "inode/directory";
+ //Special directory icons
+ QString name = this->fileName().toLower();
+ if(name=="desktop"){ icon = "user-desktop"; }
+ else if(name=="tmp"){ icon = "folder-temp"; }
+ else if(name=="video" || name=="videos"){ icon = "folder-video"; }
+ else if(name=="music" || name=="audio"){ icon = "folder-sound"; }
+ else if(name=="projects" || name=="devel"){ icon = "folder-development"; }
+ else if(name=="notes"){ icon = "folder-txt"; }
+ else if(name=="downloads"){ icon = "folder-downloads"; }
+ else if(name=="documents"){ icon = "folder-documents"; }
+ else if(name=="images" || name=="pictures"){ icon = "folder-image"; }
+ else if( !this->isReadable() ){ icon = "folder-locked"; }
+ }else if( this->suffix()=="desktop"){
+ mime = "application/x-desktop";
+ icon = "application-x-desktop"; //default value
+ bool ok = false;
+ desk = LXDG::loadDesktopFile(this->absoluteFilePath(), ok);
+ if(ok){
+ //use the specific desktop file info (if possible)
+ if(!desk.icon.isEmpty()){ icon = desk.icon; }
+ }
+ }else{
+ //Generic file, just determine the mimetype
+ mime = LXDG::findAppMimeForFile(this->fileName());
+ }
+}
+LFileInfo::LFileInfo(QString filepath){ //overloaded contructor
+ this->setFile(filepath);
+ loadExtraInfo();
+}
+LFileInfo::LFileInfo(QFileInfo info){ //overloaded contructor
+ this->swap(info); //use the given QFileInfo without re-loading it
+ loadExtraInfo();
+}
+
+//Functions for accessing the extra information
+// -- Return the mimetype for the file
+QString LFileInfo::mimetype(){
+ if(mime=="inode/directory"){ return ""; }
+ else{ return mime; }
+}
+
+// -- Return the icon to use for this file
+QString LFileInfo::iconfile(){
+ if(!icon.isEmpty()){
+ return icon;
+ }else{
+ if(!mime.isEmpty()){
+ QString tmp = mime;
+ tmp.replace("/","-");
+ return tmp;
+ }else if(this->isExecutable()){
+ return "application-x-executable";
+ }
+ }
+ return ""; //Fall back to nothing
+}
+
+// -- Check if this is an XDG desktop file
+bool LFileInfo::isDesktopFile(){
+ return (!desk.filePath.isEmpty());
+}
+
+// -- Allow access to the XDG desktop data structure
+XDGDesktop* LFileInfo::XDG(){
+ return &desk;
+}
+
+// -- Check if this is a readable image file (for thumbnail support)
+bool LFileInfo::isImage(){
+ if(!mime.startsWith("image/")){ return false; } //quick return for non-image files
+ //Check the Qt subsystems to see if this image file can be read
+ return ( !LUtils::imageExtensions().filter(this->suffix().toLower()).isEmpty() );
+}
+
+bool LFileInfo::isAVFile(){
+ return (mime.startsWith("audio/") || mime.startsWith("video/") );
+}
+
+
+//==== LXDG Functions ====
+XDGDesktop LXDG::loadDesktopFile(QString filePath, bool& ok){
+ //Create the outputs
+ ok=false;
+ XDGDesktop DF;
+ DF.isHidden=false;
+ DF.useTerminal=false;
+ DF.startupNotify=false;
+ DF.type = XDGDesktop::APP;
+ DF.filePath = filePath;
+ DF.lastRead = QDateTime::currentDateTime();
+ DF.exec = DF.tryexec = ""; // just to make sure this is initialized
+
+ //Get the current localization code
+ QString lang = QLocale::system().name(); //lang code
+ QString slang = lang.section("_",0,0); //short lang code
+
+ //Read in the File
+ bool insection=false;
+ bool inaction=false;
+ QStringList file = LUtils::readFile(filePath);
+ if(file.isEmpty()){ return DF; }
+ //if(filePath.contains("pcbsd")){ qDebug() << "Check File:" << filePath << lang << slang; }
+ XDGDesktopAction CDA; //current desktop action
+ for(int i=0; i<file.length(); i++){
+ QString line = file[i];
+ //if(filePath.contains("pcbsd")){ qDebug() << " - Check Line:" << line << inaction << insection; }
+ //Check if this is the end of a section
+ if(line.startsWith("[") && inaction){
+ insection=false; inaction=false;
+ //Add the current Action structure to the main desktop structure if appropriate
+ if(!CDA.ID.isEmpty()){ DF.actions << CDA; CDA = XDGDesktopAction(); }
+ }else if(line.startsWith("[")){ insection=false; inaction = false; }
+ //Now check if this is the beginning of a section
+ if(line=="[Desktop Entry]"){ insection=true; continue; }
+ else if(line.startsWith("[Desktop Action ")){
+ //Grab the ID of the action out of the label
+ CDA.ID = line.section("]",0,0).section("Desktop Action",1,1).simplified();
+ inaction = true;
+ continue;
+ }else if( (!insection && !inaction) || line.startsWith("#")){ continue; }
+ //Now parse out the file
+ line = line.simplified();
+ QString var = line.section("=",0,0).simplified();
+ QString loc = var.section("[",1,1).section("]",0,0).simplified(); // localization
+ var = var.section("[",0,0).simplified(); //remove the localization
+ QString val = line.section("=",1,50).simplified();
+ //if(filePath.contains("pcbsd")){ qDebug() << " -- " << var << val << loc; }
+ //-------------------
+ if(var=="Name"){
+ if(insection){
+ if(DF.name.isEmpty() && loc.isEmpty()){ DF.name = val; }
+ else if(DF.name.isEmpty() && loc==slang){ DF.name = val; } //short locale code
+ else if(loc == lang){ DF.name = val; }
+ }else if(inaction){
+ if(CDA.name.isEmpty() && loc.isEmpty()){ CDA.name = val; }
+ else if(CDA.name.isEmpty() && loc==slang){ CDA.name = val; } //short locale code
+ else if(loc == lang){ CDA.name = val; }
+ }
+ //hasName = true;
+ }else if(var=="GenericName" && insection){
+ if(DF.genericName.isEmpty() && loc.isEmpty()){ DF.genericName = val; }
+ else if(DF.genericName.isEmpty() && loc==slang){ DF.genericName = val; } //short locale code
+ else if(loc == lang){ DF.genericName = val; }
+ }else if(var=="Comment" && insection){
+ if(DF.comment.isEmpty() && loc.isEmpty()){ DF.comment = val; }
+ else if(DF.comment.isEmpty() && loc==slang){ DF.comment = val; } //short locale code
+ else if(loc == lang){ DF.comment = val; }
+ }else if(var=="Icon"){
+ if(insection){
+ if(DF.icon.isEmpty() && loc.isEmpty()){ DF.icon = val; }
+ else if(DF.icon.isEmpty() && loc==slang){ DF.icon = val; } //short locale code
+ else if(loc == lang){ DF.icon = val; }
+ }else if(inaction){
+ if(CDA.icon.isEmpty() && loc.isEmpty()){ CDA.icon = val; }
+ else if(CDA.icon.isEmpty() && loc==slang){ CDA.icon = val; } //short locale code
+ else if(loc == lang){ CDA.icon = val; }
+ }
+ }
+ else if( (var=="TryExec") && (DF.tryexec.isEmpty()) && insection) { DF.tryexec = val; }
+ else if(var=="Exec"){
+ if(insection && DF.exec.isEmpty() ){ DF.exec = val; }
+ else if(inaction && CDA.exec.isEmpty() ){ CDA.exec = val; }
+ }
+ else if( (var=="Path") && (DF.path.isEmpty() ) && insection){ DF.path = val; }
+ else if(var=="NoDisplay" && !DF.isHidden && insection){ DF.isHidden = (val.toLower()=="true"); }
+ else if(var=="Hidden" && !DF.isHidden && insection){ DF.isHidden = (val.toLower()=="true"); }
+ else if(var=="Categories" && insection){ DF.catList = val.split(";",QString::SkipEmptyParts); }
+ else if(var=="OnlyShowIn" && insection){ DF.showInList = val.split(";",QString::SkipEmptyParts); }
+ else if(var=="NotShowIn" && insection){ DF.notShowInList = val.split(";",QString::SkipEmptyParts); }
+ else if(var=="Terminal" && insection){ DF.useTerminal= (val.toLower()=="true"); }
+ else if(var=="Actions" && insection){ DF.actionList = val.split(";",QString::SkipEmptyParts); }
+ else if(var=="MimeType" && insection){ DF.mimeList = val.split(";",QString::SkipEmptyParts); }
+ else if(var=="Keywords" && insection){
+ if(DF.keyList.isEmpty() && loc.isEmpty()){ DF.keyList = val.split(";",QString::SkipEmptyParts); }
+ else if(loc == lang){ DF.keyList = val.split(";",QString::SkipEmptyParts); }
+ }
+ else if(var=="StartupNotify" && insection){ DF.startupNotify = (val.toLower()=="true"); }
+ else if(var=="StartupWMClass" && insection){ DF.startupWM = val; }
+ else if(var=="URL" && insection){ DF.url = val;}
+ else if(var=="Type" && insection){
+ if(val.toLower()=="application"){ DF.type = XDGDesktop::APP; }
+ else if(val.toLower()=="link"){ DF.type = XDGDesktop::LINK; }
+ else if(val.toLower()=="dir"){ DF.type = XDGDesktop::DIR; }
+ else{ DF.type = XDGDesktop::BAD; } //Unknown type
+ //hasType = true;
+ }
+ } //end reading file
+ //file.close();
+ //If there are OnlyShowIn desktops listed, add them to the name
+ if( !DF.showInList.isEmpty() && !DF.showInList.contains("Lumina", Qt::CaseInsensitive) ){
+ /*QStringList added;
+ //Need to be careful about case insensitivity here - the QList functions don't understand it
+ for(int i=0; i<DF.showInList.length(); i++){
+ if(DF.showInList[i].toLower()!="lumina"){ added << DF.showInList[i]; }
+ }*/
+ //if(!added.isEmpty()){
+ DF.name.append(" ("+DF.showInList.join(", ")+")");
+ //}
+ }
+ //Quick fix for showing "wine" applications (which quite often don't list a category, or have other differences)
+ if(DF.catList.isEmpty() && filePath.contains("/wine/")){
+ DF.catList << "Wine"; //Internal Lumina category only (not in XDG specs as of 11/14/14)
+ //Also add a fix for the location of Wine icons
+ if( !DF.icon.isEmpty() ){
+ QStringList sizes; sizes << "256x256" << "128x128" << "64x64" << "48x48" << "32x32" << "16x16";
+ QString upath = QDir::homePath()+"/.local/share/icons/hicolor/%1/apps/%2.png";
+ //qDebug() << "Wine App: Check icon" << upath;
+ for(int i=0; i<sizes.length(); i++){
+ if( QFile::exists(upath.arg(sizes[i],DF.icon)) ){
+ DF.icon = upath.arg(sizes[i],DF.icon);
+ //qDebug() << " - Found Icon:" << DF.icon;
+ break;
+ }
+ }
+ }
+ }
+ //Return the structure
+ //if (hasName && hasType) ok = true; //without Name and Type, the structure cannot be a valid .desktop file
+ ok = true; //was able to open/read the file - validity determined later
+ return DF;
+}
+
+bool LXDG::saveDesktopFile(XDGDesktop dFile, bool merge){
+ qDebug() << "Save Desktop File:" << dFile.filePath << "Merge:" << merge;
+ bool autofile = dFile.filePath.contains("/autostart/"); //use the "Hidden" field instead of the "NoDisplay"
+ int insertloc = -1;
+ QStringList info;
+ if(QFile::exists(dFile.filePath) && merge){
+ //Load the existing file and merge in in any changes
+ info = LUtils::readFile(dFile.filePath);
+ //set a couple flags based on the contents before we start iterating through
+ // - determine if a translated field was changed (need to remove all the now-invalid translations)
+ bool clearName, clearComment, clearGName;
+ QString tmp = "";
+ if(!info.filter("Name=").isEmpty()){ tmp = info.filter("Name=").first().section("=",1,50); }
+ clearName=(tmp!=dFile.name);
+ tmp.clear();
+ if(!info.filter("Comment=").isEmpty()){ tmp = info.filter("Comment=").first().section("=",1,50); }
+ clearComment=(tmp!=dFile.comment);
+ tmp.clear();
+ if(!info.filter("GenericName=").isEmpty()){ tmp = info.filter("GenericName=").first().section("=",1,50); }
+ clearGName=(tmp!=dFile.genericName);
+ //Now start iterating through the file and changing fields as necessary
+ bool insection = false;
+ for(int i=0; i<info.length(); i++){
+ if(info[i]=="[Desktop Entry]"){
+ insection = true;
+ continue;
+ }else if(info[i].startsWith("[")){
+ if(insection){ insertloc = i; } //save this location for later insertions
+ insection = false;
+ continue;
+ }
+ if(!insection || info[i].isEmpty() || info[i].section("#",0,0).simplified().isEmpty()){ continue; }
+ QString var = info[i].section("=",0,0);
+ QString val = info[i].section("=",1,50).simplified();
+ //NOTE: Clear the dFile variable as it is found/set in the file (to keep track of what has been used already)
+ // For boolian values, set them to false
+ // --LOCALIZED VALUES --
+ if(var.startsWith("Name")){
+ if(var.contains("[") && clearName){ info.removeAt(i); i--; continue;}
+ else if(!var.contains("[")){ info[i] = var+"="+dFile.name; dFile.name.clear(); }
+ }else if(var.startsWith("GenericName")){
+ if(var.contains("[") && clearGName){ info.removeAt(i); i--; continue;}
+ else if(!var.contains("[")){ info[i] = var+"="+dFile.genericName; dFile.genericName.clear(); }
+ }else if(var.startsWith("Comment")){
+ if(var.contains("[") && clearComment){ info.removeAt(i); i--; continue;}
+ else if(!var.contains("[")){ info[i] = var+"="+dFile.comment; dFile.comment.clear(); }
+
+ // --STRING/LIST VALUES--
+ }else if(var=="Exec"){ info[i] = var+"="+dFile.exec; dFile.exec.clear(); }
+ else if(var=="TryExec"){ info[i] = var+"="+dFile.tryexec; dFile.tryexec.clear(); }
+ else if(var=="Path"){ info[i] = var+"="+dFile.path; dFile.path.clear(); }
+ else if(var=="Icon"){ info[i] = var+"="+dFile.icon; dFile.icon.clear(); }
+ else if(var=="StartupWMClass"){ info[i] = var+"="+dFile.startupWM; dFile.startupWM.clear(); }
+ else if(var=="MimeType"){ info[i] = var+"="+dFile.mimeList.join(";"); dFile.mimeList.clear(); }
+ else if(var=="Categories"){ info[i] = var+"="+dFile.catList.join(";"); dFile.catList.clear(); }
+ else if(var=="Keywords"){ info[i] = var+"="+dFile.keyList.join(";"); dFile.keyList.clear(); }
+ else if(var=="Actions"){ info[i] = var+"="+dFile.actionList.join(";"); dFile.actionList.clear(); }
+ else if(var=="OnlyShowIn"){ info[i] = var+"="+dFile.showInList.join(";"); dFile.showInList.clear(); }
+ else if(var=="NotShowIn"){ info[i] = var+"="+dFile.notShowInList.join(";"); dFile.notShowInList.clear(); }
+ else if(var=="URL"){ info[i] = var+"="+dFile.url; dFile.url.clear(); }
+
+ // --BOOLIAN VALUES--
+ else if(var=="Hidden"){
+ if(!autofile){ info.removeAt(i); i--; continue; }
+ else{ info[i] = var+"="+(dFile.isHidden ? "true": "false"); dFile.isHidden=false;}
+ }else if(var=="NoDisplay"){
+ if(autofile){ info.removeAt(i); i--; continue; }
+ else{ info[i] = var+"="+(dFile.isHidden ? "true": "false"); dFile.isHidden=false;}
+ }else if(var=="Terminal"){
+ info[i] = var+"="+(dFile.useTerminal ? "true": "false"); dFile.useTerminal=false;
+ }else if(var=="StartupNotify"){
+ info[i] = var+"="+(dFile.startupNotify ? "true": "false"); dFile.startupNotify=false;
+ }
+ // Remove any lines that have been un-set or removed from the file
+ if(info[i].section("=",1,50).simplified().isEmpty()){ info.removeAt(i); i--; }
+ }
+
+ }else{
+ //Just write a new file and overwrite any old one
+ // (pre-set some values here which are always required)
+ info << "[Desktop Entry]";
+ info << "Version=1.0";
+ if(dFile.type==XDGDesktop::APP){ info << "Type=Application"; }
+ else if(dFile.type==XDGDesktop::LINK){ info << "Type=Link"; }
+ else if(dFile.type==XDGDesktop::DIR){ info << "Type=Dir"; }
+ }
+
+ if(insertloc<0){ insertloc = info.size(); }//put it at the end
+ //Now add in any items that did not exist in the original file
+ if( !dFile.exec.isEmpty() ){ info.insert(insertloc,"Exec="+dFile.exec); }
+ if( !dFile.tryexec.isEmpty() ){ info.insert(insertloc,"TryExec="+dFile.tryexec); }
+ if( !dFile.path.isEmpty() ){ info.insert(insertloc,"Path="+dFile.path); }
+ if( !dFile.icon.isEmpty() ){ info.insert(insertloc,"Icon="+dFile.icon); }
+ if( !dFile.name.isEmpty() ){ info.insert(insertloc,"Name="+dFile.name); }
+ if( !dFile.genericName.isEmpty() ){ info.insert(insertloc,"GenericName="+dFile.genericName); }
+ if( !dFile.comment.isEmpty() ){ info.insert(insertloc,"Comment="+dFile.comment); }
+ if( !dFile.startupWM.isEmpty() ){ info.insert(insertloc,"StartupWMClass="+dFile.startupWM); }
+ if( !dFile.mimeList.isEmpty() ){ info.insert(insertloc,"MimeType="+dFile.mimeList.join(";")); }
+ if( !dFile.catList.isEmpty() ){ info.insert(insertloc,"Categories="+dFile.catList.join(";")); }
+ if( !dFile.keyList.isEmpty() ){ info.insert(insertloc,"Keywords="+dFile.keyList.join(";")); }
+ if( !dFile.actionList.isEmpty() ){ info.insert(insertloc,"Actions="+dFile.actionList.join(";")); }
+ if( !dFile.showInList.isEmpty() ){ info.insert(insertloc,"OnlyShowIn="+dFile.showInList.join(";")); }
+ else if( !dFile.notShowInList.isEmpty() ){ info.insert(insertloc,"NotShowIn="+dFile.notShowInList.join(";")); }
+ if( !dFile.url.isEmpty() ){ info.insert(insertloc,"URL="+dFile.url); }
+ if( dFile.isHidden && autofile ){ info.insert(insertloc,"Hidden=true"); }
+ else if(dFile.isHidden){ info.insert(insertloc,"NoDisplay=true"); }
+ if( dFile.useTerminal){ info.insert(insertloc,"Terminal=true"); }
+ if( dFile.startupNotify ){ info.insert(insertloc,"StartupNotify=true"); }
+
+ //Now save the file
+ return LUtils::writeFile(dFile.filePath, info, true);
+
+}
+
+bool LXDG::checkValidity(XDGDesktop dFile, bool showAll){
+ bool ok=true;
+ bool DEBUG = false;
+ if(DEBUG){ qDebug() << "[LXDG] Check File validity:" << dFile.name << dFile.filePath; }
+ switch (dFile.type){
+ case XDGDesktop::BAD:
+ ok=false;
+ if(DEBUG){ qDebug() << " - Bad file type"; }
+ break;
+ case XDGDesktop::APP:
+ if(!dFile.tryexec.isEmpty() && !LXDG::checkExec(dFile.tryexec)){ ok=false; if(DEBUG){ qDebug() << " - tryexec does not exist";} }
+ else if(dFile.exec.isEmpty() || dFile.name.isEmpty()){ ok=false; if(DEBUG){ qDebug() << " - exec or name is empty";} }
+ else if(!LXDG::checkExec(dFile.exec.section(" ",0,0,QString::SectionSkipEmpty)) ){ ok=false; if(DEBUG){ qDebug() << " - first exec binary does not exist";} }
+ break;
+ case XDGDesktop::LINK:
+ ok = !dFile.url.isEmpty();
+ if(DEBUG && !ok){ qDebug() << " - Link with missing URL"; }
+ break;
+ case XDGDesktop::DIR:
+ ok = !dFile.path.isEmpty();
+ if(DEBUG && !ok){ qDebug() << " - Dir with missing path"; }
+ break;
+ default:
+ ok=false;
+ if(DEBUG){ qDebug() << " - Unknown file type"; }
+ }
+ if(!showAll){
+ if(!dFile.showInList.isEmpty()){ ok = dFile.showInList.contains("Lumina", Qt::CaseInsensitive); }
+ else if(!dFile.notShowInList.isEmpty()){ ok = !dFile.notShowInList.contains("Lumina",Qt::CaseInsensitive); }
+ else if(dFile.name.isEmpty()){ ok = false; }
+ }
+ return ok;
+}
+
+bool LXDG::checkExec(QString exec){
+ //Return true(good) or false(bad)
+ if(exec.startsWith("/")){ return QFile::exists(exec); }
+ else{
+ QStringList paths = QString(getenv("PATH")).split(":");
+ for(int i=0; i<paths.length(); i++){
+ if(QFile::exists(paths[i]+"/"+exec)){ return true; }
+ }
+ }
+ return false; //could not find the executable in the current path(s)
+}
+
+QStringList LXDG::systemApplicationDirs(){
+ //Returns a list of all the directories where *.desktop files can be found
+ QStringList appDirs = QString(getenv("XDG_DATA_HOME")).split(":");
+ appDirs << QString(getenv("XDG_DATA_DIRS")).split(":");
+ if(appDirs.isEmpty()){ appDirs << "/usr/local/share" << "/usr/share" << LOS::AppPrefix()+"/share" << LOS::SysPrefix()+"/share" << L_SHAREDIR; }
+ appDirs.removeDuplicates();
+ //Now create a valid list
+ QStringList out;
+ for(int i=0; i<appDirs.length(); i++){
+ if( QFile::exists(appDirs[i]+"/applications") ){
+ out << appDirs[i]+"/applications";
+ //Also check any subdirs within this directory
+ // (looking at you KDE - stick to the standards!!)
+ out << LUtils::listSubDirectories(appDirs[i]+"/applications");
+ //QDir dir(appDirs[i]+"/applications");
+ //QStringList subs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ //qDebug() << "Adding subdirectories:" << appDirs[i]+"/applications/["+subs.join(", ")+"]";
+ //for(int s=0; s<subs.length(); s++){ out << dir.absoluteFilePath(subs[s]); }
+ }
+ }
+ //qDebug() << "System Application Dirs:" << out;
+ return out;
+}
+
+QList<XDGDesktop> LXDG::systemDesktopFiles(bool showAll, bool showHidden){
+ //Returns a list of all the unique *.desktop files that were found
+ QStringList appDirs = LXDG::systemApplicationDirs();
+ QStringList found; //for avoiding duplicate apps
+ QList<XDGDesktop> out;
+ bool ok; //for internal use only
+ for(int i=0; i<appDirs.length(); i++){
+ QDir dir(appDirs[i]);
+ QStringList apps = dir.entryList(QStringList() << "*.desktop",QDir::Files, QDir::Name);
+ for(int a=0; a<apps.length(); a++){
+ ok=false;
+ XDGDesktop dFile = LXDG::loadDesktopFile(dir.absoluteFilePath(apps[a]),ok);
+ if( LXDG::checkValidity(dFile, showAll) ){
+ if( !found.contains(dFile.name) && (!dFile.isHidden || showHidden) ){
+ out << dFile;
+ found << dFile.name;
+ }
+ }
+ }
+ }
+ return out;
+}
+
+QHash<QString,QList<XDGDesktop> > LXDG::sortDesktopCats(QList<XDGDesktop> apps){
+ //Sort the list of applications into their different categories (main categories only)
+ //Create the category lists
+ QList<XDGDesktop> multimedia, dev, ed, game, graphics, network, office, science, settings, sys, utility, other, wine;
+ //Sort the apps into the lists
+ for(int i=0; i<apps.length(); i++){
+ if(apps[i].catList.contains("AudioVideo")){ multimedia << apps[i]; }
+ else if(apps[i].catList.contains("Development")){ dev << apps[i]; }
+ else if(apps[i].catList.contains("Education")){ ed << apps[i]; }
+ else if(apps[i].catList.contains("Game")){ game << apps[i]; }
+ else if(apps[i].catList.contains("Graphics")){ graphics << apps[i]; }
+ else if(apps[i].catList.contains("Network")){ network << apps[i]; }
+ else if(apps[i].catList.contains("Office")){ office << apps[i]; }
+ else if(apps[i].catList.contains("Science")){ science << apps[i]; }
+ else if(apps[i].catList.contains("Settings")){ settings << apps[i]; }
+ else if(apps[i].catList.contains("System")){ sys << apps[i]; }
+ else if(apps[i].catList.contains("Utility")){ utility << apps[i]; }
+ else if(apps[i].catList.contains("Wine")){ wine << apps[i]; }
+ else{ other << apps[i]; }
+ }
+ //Now create the output hash
+ QHash<QString,QList<XDGDesktop> > out;
+ if(!multimedia.isEmpty()){ out.insert("Multimedia", LXDG::sortDesktopNames(multimedia)); }
+ if(!dev.isEmpty()){ out.insert("Development", LXDG::sortDesktopNames(dev)); }
+ if(!ed.isEmpty()){ out.insert("Education", LXDG::sortDesktopNames(ed)); }
+ if(!game.isEmpty()){ out.insert("Game", LXDG::sortDesktopNames(game)); }
+ if(!graphics.isEmpty()){ out.insert("Graphics", LXDG::sortDesktopNames(graphics)); }
+ if(!network.isEmpty()){ out.insert("Network", LXDG::sortDesktopNames(network)); }
+ if(!office.isEmpty()){ out.insert("Office", LXDG::sortDesktopNames(office)); }
+ if(!science.isEmpty()){ out.insert("Science", LXDG::sortDesktopNames(science)); }
+ if(!settings.isEmpty()){ out.insert("Settings", LXDG::sortDesktopNames(settings)); }
+ if(!sys.isEmpty()){ out.insert("System", LXDG::sortDesktopNames(sys)); }
+ if(!utility.isEmpty()){ out.insert("Utility", LXDG::sortDesktopNames(utility)); }
+ if(!wine.isEmpty()){ out.insert("Wine", LXDG::sortDesktopNames(wine)); }
+ if(!other.isEmpty()){ out.insert("Unsorted", LXDG::sortDesktopNames(other)); }
+ //return the resulting hash
+ return out;
+}
+
+//Return the icon to use for the given category
+QString LXDG::DesktopCatToIcon(QString cat){
+ QString icon = "applications-other";
+ if(cat=="Multimedia"){ icon = "applications-multimedia"; }
+ else if(cat=="Development"){ icon = "applications-development"; }
+ else if(cat=="Education"){ icon = "applications-education"; }
+ else if(cat=="Game"){ icon = "applications-games"; }
+ else if(cat=="Graphics"){ icon = "applications-graphics"; }
+ else if(cat=="Network"){ icon = "applications-internet"; }
+ else if(cat=="Office"){ icon = "applications-office"; }
+ else if(cat=="Science"){ icon = "applications-science"; }
+ else if(cat=="Settings"){ icon = "preferences-system"; }
+ else if(cat=="System"){ icon = "applications-system"; }
+ else if(cat=="Utility"){ icon = "applications-utilities"; }
+ else if(cat=="Wine"){ icon = "wine"; }
+ return icon;
+}
+
+QList<XDGDesktop> LXDG::sortDesktopNames(QList<XDGDesktop> apps){
+ //Sort the list by name of the application
+ QHash<QString, XDGDesktop> sorter;
+ for(int i=0; i<apps.length(); i++){
+ sorter.insert(apps[i].name.toLower(), apps[i]);
+ }
+ QStringList keys = sorter.keys();
+ keys.sort();
+ //Re-assemble the output list
+ QList<XDGDesktop> out;
+ for(int i=0; i<keys.length(); i++){
+ out << sorter[keys[i]];
+ }
+ return out;
+}
+
+QString LXDG::getDesktopExec(XDGDesktop app, QString ActionID){
+ //Generate the executable line for the application
+ QString out;
+ QString exec = app.exec;
+ if( !ActionID.isEmpty() ){
+ //Go through and grab the proper exec for the listed action
+ for(int i=0; i<app.actions.length(); i++){
+ if(app.actions[i].ID == ActionID){
+ exec = app.actions[i].exec;
+ break;
+ }
+ }
+ }
+
+ if(exec.isEmpty()){ return ""; }
+ else if(app.useTerminal){
+ out = "xterm -lc -e "+exec;
+ }else{
+ out =exec;
+ }
+ //Now perform any of the XDG flag substitutions as appropriate (9/2014 standards)
+ if(out.contains("%i") && !app.icon.isEmpty() ){ out.replace("%i", "--icon \'"+app.icon+"\'"); }
+ if(out.contains("%c")){
+ if(!app.name.isEmpty()){ out.replace("%c", "\'"+app.name+"\'"); }
+ else if(!app.genericName.isEmpty()){ out.replace("%c", "\'"+app.genericName+"\'"); }
+ else{ out.replace("%c", "\'"+app.filePath.section("/",-1).section(".desktop",0,0)+"\'"); }
+ }
+ if(out.contains("%k")){ out.replace("%k", "\'"+app.filePath+"\'"); }
+ return out;
+}
+
+void LXDG::setEnvironmentVars(){
+ //Set the default XDG environment variables if not already set
+ setenv("XDG_DATA_HOME",QString(QDir::homePath()+"/.local/share").toUtf8(), 0);
+ setenv("XDG_CONFIG_HOME",QString(QDir::homePath()+"/.config").toUtf8(), 0);
+ setenv("XDG_DATA_DIRS","/usr/local/share:/usr/share", 0);
+ setenv("XDG_CONFIG_DIRS","/etc/xdg:/usr/local/etc/xdg", 0);
+ setenv("XDG_CACHE_HOME",QString(QDir::homePath()+"/.cache").toUtf8(), 0);
+ //Don't set "XDG_RUNTIME_DIR" yet - need to look into the specs
+}
+
+QIcon LXDG::findIcon(QString iconName, QString fallback){
+ //NOTE: This was re-written on 11/10/15 to avoid using the QIcon::fromTheme() framework
+ // -- Too many issues with SVG files and/or search paths with the built-in system
+
+ //Check if the icon is an absolute path and exists
+ bool DEBUG =false;
+ if(DEBUG){ qDebug() << "[LXDG] Find icon for:" << iconName; }
+ if(QFile::exists(iconName) && iconName.startsWith("/")){ return QIcon(iconName); }
+ else if(iconName.startsWith("/")){ iconName.section("/",-1); } //Invalid absolute path, just look for the icon
+ //Check if the icon is actually given
+ if(iconName.isEmpty()){
+ if(fallback.isEmpty()){ return QIcon(); }
+ else{ return LXDG::findIcon(fallback, ""); }
+ }
+ //Now try to find the icon from the theme
+ if(DEBUG){ qDebug() << "[LXDG] Start search for icon"; }
+ //Get the currently-set theme
+ QString cTheme = QIcon::themeName();
+ if(cTheme.isEmpty()){
+ QIcon::setThemeName("oxygen");
+ cTheme = "oxygen";
+ }
+ //Make sure the current search paths correspond to this theme
+ if( QDir::searchPaths("icontheme").filter("/"+cTheme+"/").isEmpty() ){
+ //Need to reset search paths: setup the "icontheme" "oxygen" and "fallback" sets
+ // - Get all the base icon directories
+ QStringList paths;
+ paths << QDir::homePath()+"/.icons/"; //ordered by priority - local user dirs first
+ QStringList xdd = QString(getenv("XDG_DATA_HOME")).split(":");
+ xdd << QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int i=0; i<xdd.length(); i++){
+ if(QFile::exists(xdd[i]+"/icons")){ paths << xdd[i]+"/icons/"; }
+ }
+ //Now load all the dirs into the search paths
+ QStringList theme, oxy, fall;
+ for(int i=0; i<paths.length(); i++){
+ theme << getChildIconDirs( paths[i]+cTheme);
+ oxy << getChildIconDirs(paths[i]+"oxygen"); //Lumina base icon set
+ fall << getChildIconDirs(paths[i]+"hicolor"); //XDG fallback (apps add to this)
+ }
+ //fall << LOS::AppPrefix()+"share/pixmaps"; //always use this as well as a final fallback
+ QDir::setSearchPaths("icontheme", theme);
+ QDir::setSearchPaths("oxygen", oxy);
+ QDir::setSearchPaths("fallback", fall);
+ //qDebug() << "Setting Icon Search Paths:" << "\nicontheme:" << theme << "\noxygen:" << oxy << "\nfallback:" << fall;
+ }
+ //Find the icon in the search paths
+ QIcon ico;
+ QStringList srch; srch << "icontheme" << "oxygen" << "fallback";
+ for(int i=0; i<srch.length() && ico.isNull(); i++){
+ //Look for a svg first
+ if(QFile::exists(srch[i]+":"+iconName+".svg") ){
+ //Be careful about how an SVG is loaded - needs to render the image onto a paint device
+ QSvgRenderer svg;
+ if( svg.load(srch[i]+":"+iconName+".svg") ){
+ //Could be loaded - now check that it is version 1.1+ (Qt has issues with 1.0? (LibreOffice Icons) )
+ float version = 1.1; //only downgrade files that explicitly set the version as older
+ QString svginfo = LUtils::readFile(srch[i]+":"+iconName+".svg").join("\n").section("<svg",1,1).section(">",0,0);
+ svginfo.replace("\t"," "); svginfo.replace("\n"," ");
+ if(svginfo.contains(" version=")){ version = svginfo.section(" version=\"",1,1).section("\"",0,0).toFloat(); }
+ if(version>=1.1){
+ ico.addFile(srch[i]+":"+iconName+".svg"); //could be loaded/parsed successfully
+ }else{
+ //qDebug() << "Old SVG Version file:" << iconName+".svg Theme:" << srch[i];
+ //qDebug() << "SVGInfo:" << svginfo;
+ }
+ }else{
+ qDebug() << "Found bad SVG file:" << iconName+".svg Theme:" << srch[i];
+ }
+ }
+ if(QFile::exists(srch[i]+":"+iconName+".png")){
+ //simple PNG image - load directly into the QIcon structure
+ ico.addFile(srch[i]+":"+iconName+".png");
+ }
+
+ }
+ //If still no icon found, look for any image format inthe "pixmaps" directory
+ if(ico.isNull()){
+ if(QFile::exists(LOS::AppPrefix()+"share/pixmaps/"+iconName)){
+ ico.addFile(LOS::AppPrefix()+"share/pixmaps/"+iconName);
+ }else{
+ //Need to scan for any close match in the directory
+ QDir pix(LOS::AppPrefix()+"share/pixmaps");
+ QStringList formats = LUtils::imageExtensions();
+ QStringList found = pix.entryList(QStringList() << iconName, QDir::Files, QDir::Unsorted);
+ if(found.isEmpty()){ found = pix.entryList(QStringList() << iconName+"*", QDir::Files, QDir::Unsorted); }
+ //qDebug() << "Found pixmaps:" << found << formats;
+ //Use the first one found that is a valid format
+ for(int i=0; i<found.length(); i++){
+ if( formats.contains(found[i].section(".",-1).toLower()) ){
+ ico.addFile( pix.absoluteFilePath(found[i]) );
+ break;
+ }
+ }
+
+ }
+ }
+ //Use the fallback icon if necessary
+ if(ico.isNull() && !fallback.isEmpty()){
+ ico = LXDG::findIcon(fallback,"");
+ }
+ if(ico.isNull()){
+ qDebug() << "Could not find icon:" << iconName << fallback;
+ }
+ //Return the icon
+ return ico;
+}
+
+QStringList LXDG::getChildIconDirs(QString parent){
+ //This is a recursive function that returns the absolute path(s) of directories with *.png files
+ QDir D(parent);
+ QStringList out;
+ QStringList dirs = D.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ if(!dirs.isEmpty() && (dirs.contains("32x32") || dirs.contains("scalable")) ){
+ //Need to sort these directories by image size
+ //qDebug() << " - Parent:" << parent << "Dirs:" << dirs;
+ for(int i=0; i<dirs.length(); i++){
+ if(dirs[i].contains("x")){ dirs[i].prepend( QString::number(10-dirs[i].section("x",0,0).length())+QString::number(10-dirs[i].at(0).digitValue())+"::::"); }
+ else{ dirs[i].prepend( "0::::"); }
+ }
+ dirs.sort();
+ for(int i=0; i<dirs.length(); i++){ dirs[i] = dirs[i].section("::::",1,50); } //chop the sorter off the front again
+ //qDebug() << "Sorted:" << dirs;
+ }
+ QStringList img = D.entryList(QStringList() << "*.png" << "*.svg", QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort);
+ if(img.length() > 0){ out << D.absolutePath(); }
+ for(int i=0; i<dirs.length(); i++){
+ img.clear();
+ img = getChildIconDirs(D.absoluteFilePath(dirs[i])); //re-use the old list variable
+ if(img.length() > 0){ out << img; }
+ }
+ return out;
+}
+
+QStringList LXDG::systemMimeDirs(){
+ //Returns a list of all the directories where *.xml MIME files can be found
+ QStringList appDirs = QString(getenv("XDG_DATA_HOME")).split(":");
+ appDirs << QString(getenv("XDG_DATA_DIRS")).split(":");
+ if(appDirs.isEmpty()){ appDirs << "/usr/local/share" << "/usr/share"; }
+ //Now create a valid list
+ QStringList out;
+ for(int i=0; i<appDirs.length(); i++){
+ if( QFile::exists(appDirs[i]+"/mime") ){
+ out << appDirs[i]+"/mime";
+ }
+ }
+ return out;
+}
+
+QIcon LXDG::findMimeIcon(QString extension){
+ QIcon ico;
+ QString mime = LXDG::findAppMimeForFile(extension);
+ if(mime.isEmpty()){ mime = LXDG::findAppMimeForFile(extension.toLower()); }
+ mime.replace("/","-"); //translate to icon mime name
+ if(!mime.isEmpty()){ ico = LXDG::findIcon(mime, "unknown");} //use the "unknown" mimetype icon as fallback
+ if(ico.isNull()){ ico = LXDG::findIcon("unknown",""); } //just in case
+ return ico;
+}
+
+QString LXDG::findAppMimeForFile(QString filename, bool multiple){
+ QString out;
+ QString extension = filename.section(".",-1);
+ if("."+extension == filename){ extension.clear(); } //hidden file without extension
+ //qDebug() << "MIME SEARCH:" << filename << extension;
+ QStringList mimefull = LXDG::loadMimeFileGlobs2();
+ QStringList mimes;
+ //Just in case the extension/filename is a mimetype itself
+ if( mimefull.filter(":"+filename+":").length() == 1){
+ return filename;
+ }
+ else if(mimefull.filter(":"+extension+":").length() == 1){
+ return extension;
+ }
+ //Look for globs at the end of the filename
+ if(!extension.isEmpty()){
+ mimes = mimefull.filter(":*."+extension);
+ //If nothing found, try a case-insensitive search
+ if(mimes.isEmpty()){ mimes = mimefull.filter(":*."+extension, Qt::CaseInsensitive); }
+ //Now ensure that the filter was accurate (*.<extention>.<something> will still be caught)
+ for(int i=0; i<mimes.length(); i++){
+ if(!filename.endsWith( mimes[i].section(":*",-1), Qt::CaseInsensitive )){ mimes.removeAt(i); i--; }
+ }
+ }
+ //Look for globs at the start of the filename
+ if(mimes.isEmpty()){
+ mimes = mimefull.filter(":"+filename.left(2)); //look for the first 2 characters initially
+ //Note: This initial filter will only work if the wildcard (*) is not within the first 2 characters of the pattern
+ //Now ensure that the filter was accurate
+ for(int i=0; i<mimes.length(); i++){
+ if(!filename.startsWith( mimes[i].section(":",3,50,QString::SectionSkipEmpty).section("*",0,0), Qt::CaseInsensitive )){ mimes.removeAt(i); i--; }
+ }
+ }
+ mimes.sort(); //this automatically puts them in weight order (100 on down)
+ QStringList matches;
+ //qDebug() << "Mimes:" << mimes;
+ for(int m=0; m<mimes.length(); m++){
+ QString mime = mimes[m].section(":",1,1,QString::SectionSkipEmpty);
+ matches << mime;
+ }
+ //qDebug() << "Matches:" << matches;
+ if(multiple && !matches.isEmpty() ){ out = matches.join("::::"); }
+ else if( !matches.isEmpty() ){ out = matches.first(); }
+ else{ //no mimetype found - assign one (internal only - no system database changes)
+ if(extension.isEmpty()){ out = "unknown/"+filename.toLower(); }
+ else{ out = "unknown/"+extension.toLower(); }
+ }
+ //qDebug() << "Out:" << out;
+ return out;
+}
+
+QStringList LXDG::findFilesForMime(QString mime){
+ QStringList out;
+ QStringList mimes = LXDG::loadMimeFileGlobs2().filter(mime);
+ for(int i=0; i<mimes.length(); i++){
+ out << mimes[i].section(":",2,2); // "*.<extension>"
+ }
+ //qDebug() << "Mime to Files:" << mime << out;
+ return out;
+}
+
+QStringList LXDG::listFileMimeDefaults(){
+ //This will spit out a itemized list of all the mimetypes and relevant info
+ // Output format: <mimetype>::::<extension>::::<default>::::<localized comment>
+ QStringList mimes = LXDG::loadMimeFileGlobs2();
+ //Remove all the application files from the list (only a single app defines/uses this type in general)
+ /*QStringList apps = mimes.filter(":application/");
+ //qDebug() << "List Mime Defaults";
+ for(int i=0; i<apps.length(); i++){ mimes.removeAll(apps[i]); }*/
+ //Now start filling the output list
+ QStringList out;
+ for(int i=0; i<mimes.length(); i++){
+ QString mimetype = mimes[i].section(":",1,1);
+ QStringList tmp = mimes.filter(mimetype);
+ //Collect all the different extensions with this mimetype
+ QStringList extlist;
+ for(int j=0; j<tmp.length(); j++){
+ mimes.removeAll(tmp[j]);
+ extlist << tmp[j].section(":",2,2);
+ }
+ extlist.removeDuplicates(); //just in case
+ //Now look for a current default for this mimetype
+ QString dapp = LXDG::findDefaultAppForMime(mimetype); //default app;
+
+ //Create the output entry
+ //qDebug() << "Mime entry:" << i << mimetype << dapp;
+ out << mimetype+"::::"+extlist.join(", ")+"::::"+dapp+"::::"+LXDG::findMimeComment(mimetype);
+
+ i--; //go back one (continue until the list is empty)
+ }
+ return out;
+}
+
+QString LXDG::findMimeComment(QString mime){
+ QString comment;
+ QStringList dirs = LXDG::systemMimeDirs();
+ QString lang = QString(getenv("LANG")).section(".",0,0);
+ QString shortlang = lang.section("_",0,0);
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/"+mime+".xml")){
+ QStringList info = LUtils::readFile(dirs[i]+"/"+mime+".xml");
+ QStringList filter = info.filter("<comment xml:lang=\""+lang+"\">");
+ //First look for a full language match, then short language, then general comment
+ if(filter.isEmpty()){ filter = info.filter("<comment xml:lang=\""+shortlang+"\">"); }
+ if(filter.isEmpty()){ filter = info.filter("<comment>"); }
+ if(!filter.isEmpty()){
+ comment = filter.first().section(">",1,1).section("</",0,0);
+ break;
+ }
+ }
+ }
+ return comment;
+}
+
+QString LXDG::findDefaultAppForMime(QString mime){
+ //First get the priority-ordered list of default file locations
+ QStringList dirs;
+ dirs << QString(getenv("XDG_CONFIG_HOME"))+"/lumina-mimeapps.list" \
+ << QString(getenv("XDG_CONFIG_HOME"))+"/mimeapps.list";
+ QStringList tmp = QString(getenv("XDG_CONFIG_DIRS")).split(":");
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/lumina-mimeapps.list"; }
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/mimeapps.list"; }
+ dirs << QString(getenv("XDG_DATA_HOME"))+"/applications/lumina-mimeapps.list" \
+ << QString(getenv("XDG_DATA_HOME"))+"/applications/mimeapps.list";
+ tmp = QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/applications/lumina-mimeapps.list"; }
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/applications/mimeapps.list"; }
+
+ //Now go through all the files in order of priority until a default is found
+ QString cdefault;
+ QStringList white; //lists to keep track of during the search (black unused at the moment)
+ for(int i=0; i<dirs.length() && cdefault.isEmpty(); i++){
+ if(!QFile::exists(dirs[i])){ continue; }
+ QStringList info = LUtils::readFile(dirs[i]);
+ if(info.isEmpty()){ continue; }
+ QString workdir = dirs[i].section("/",0,-1); //just the directory
+ int def = info.indexOf("[Default Applications]"); //find this line to start on
+ if(def>=0){
+ for(int d=def+1; d<info.length(); d++){
+ if(info[d].startsWith("[")){ break; } //starting a new section now - finished with defaults
+ if(info[d].contains(mime+"=")){
+ white << info[d].section("=",1,50).split(";");
+ break;
+ }
+ }
+ }
+ // Now check for any white-listed files in this work dir
+ // find the full path to the file (should run even if nothing in this file)
+ //qDebug() << "WhiteList:" << white;
+ for(int w=0; w<white.length(); w++){
+ if(white[w].isEmpty()){ continue; }
+ //First check for absolute paths to *.desktop file
+ if( white[w].startsWith("/") ){
+ if( QFile::exists(white[w]) ){ cdefault=white[w]; break; }
+ else{ white.removeAt(w); w--; } //invalid file path - remove it from the list
+ }
+ //Now check for relative paths to file (in current priority-ordered work dir)
+ else if( QFile::exists(workdir+"/"+white[w]) ){ cdefault=workdir+"/"+white[w]; break; }
+ //Now go through the XDG DATA dirs and see if the file is in there
+ else{
+ QStringList xdirs;
+ xdirs << QString(getenv("XDG_DATA_HOME"))+"/applications/";
+ tmp = QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int t=0; t<tmp.length(); t++){ xdirs << tmp[t]+"/applications/"; }
+ //Now scan these dirs
+ bool found = false;
+ //qDebug() << "Scan dirs:" << white[w] << xdirs;
+ for(int x=0; x<xdirs.length() && !found; x++){
+ if(QFile::exists(xdirs[x]+white[w])){cdefault=xdirs[x]+white[w]; found = true; }
+ }
+ if(found){ break; }
+ }
+ }
+ /* WRITTEN BUT UNUSED CODE FOR MIMETYPE ASSOCIATIONS
+ //Skip using this because it is simply an alternate/unsupported standard that conflicts with
+ the current mimetype database standards. It is better/faster to parse 1 or 2 database glob files
+ rather than have to iterate through hundreds of *.desktop files *every* time you need to
+ find an application
+
+ if(addI<0 && remI<0){
+ //Simple Format: <mimetype>=<*.desktop file>;<*.desktop file>;.....
+ // (usually only one desktop file listed)
+ info = info.filter(mimetype+"=");
+ //Load the listed default(s)
+ for(int w=0; w<info.length(); w++){
+ white << info[w].section("=",1,50).split(";");
+ }
+ }else{
+ //Non-desktop specific mimetypes file: has a *very* convoluted/inefficient algorithm (required by spec)
+ if(addI<0){ addI = info.length(); } //no add section
+ if(remI<0){ remI = info.length(); } // no remove section:
+ //Whitelist items
+ for(int a=addI+1; a!=remI && a<info.length(); a++){
+ if(info[a].contains(mimetype+"=")){
+ QStringList tmp = info[a].section("=",1,50).split(";");
+ for(int t=0; t<tmp.length(); t++){
+ if(!black.contains(tmp[t])){ white << tmp[t]; } //make sure this item is not on the black list
+ }
+ break;
+ }
+ }
+ //Blacklist items
+ for(int a=remI+1; a!=addI && a<info.length(); a++){
+ if(info[a].contains(mimetype+"=")){ black << info[a].section("=",1,50).split(";"); break;}
+ }
+
+ //STEPS 3/4 not written yet
+
+ } //End of non-DE mimetypes file */
+
+ } //End loop over files
+
+ return cdefault;
+}
+
+QStringList LXDG::findAvailableAppsForMime(QString mime){
+ QStringList dirs = LXDG::systemApplicationDirs();
+ QStringList out;
+ //Loop over all possible directories that contain *.destop files
+ // and check for the mimeinfo.cache file
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/mimeinfo.cache")){
+ QStringList matches = LUtils::readFile(dirs[i]+"/mimeinfo.cache").filter(mime+"=");
+ //Find any matches for our mimetype in the cache
+ for(int j=0; j<matches.length(); j++){
+ QStringList files = matches[j].section("=",1,1).split(";",QString::SkipEmptyParts);
+ //Verify that each file exists before putting the full path to the file in the output
+ for(int m=0; m<files.length(); m++){
+ if(QFile::exists(dirs[i]+"/"+files[m])){
+ out << dirs[i]+"/"+files[m];
+ }else if(files[m].contains("-")){ //kde4-<filename> -> kde4/<filename> (stupid KDE variations!!)
+ files[m].replace("-","/");
+ if(QFile::exists(dirs[i]+"/"+files[m])){
+ out << dirs[i]+"/"+files[m];
+ }
+ }
+ }
+ }
+ }
+ }
+ //qDebug() << "Found Apps for Mime:" << mime << out << dirs;
+ return out;
+}
+
+void LXDG::setDefaultAppForMime(QString mime, QString app){
+ QString filepath = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-mimeapps.list";
+ QStringList cinfo = LUtils::readFile(filepath);
+ //If this is a new file, make sure to add the header appropriately
+ if(cinfo.isEmpty()){ cinfo << "#Automatically generated with lumina-config" << "# DO NOT CHANGE MANUALLY" << "[Default Applications]"; }
+ //Check for any current entry for this mime type
+ QStringList tmp = cinfo.filter(mime+"=");
+ int index = -1;
+ if(!tmp.isEmpty()){ index = cinfo.indexOf(tmp.first()); }
+ //Now add the new default entry (if necessary)
+ if(app.isEmpty()){
+ if(index>=0){ cinfo.removeAt(index); } //Remove entry
+ }else{
+ if(index<0){
+ cinfo << mime+"="+app+";"; //new entry
+ }else{
+ cinfo[index] = mime+"="+app+";"; //overwrite existing entry
+ }
+ }
+ LUtils::writeFile(filepath, cinfo, true);
+ return;
+}
+
+QStringList LXDG::findAVFileExtensions(){
+ //output format: QDir name filter for valid A/V file extensions
+ QStringList globs = LXDG::loadMimeFileGlobs2();
+ QStringList av = globs.filter(":audio/");
+ av << globs.filter(":video/");
+ for(int i=0; i<av.length(); i++){
+ //Just use all audio/video mimetypes (for now)
+ av[i] = av[i].section(":",2,2);
+ //Qt5 Auto detection (broken - QMediaPlayer seg faults with Qt 5.3 - 11/24/14)
+ /*if( QMultimedia::NotSupported != QMediaPlayer::hasSupport(av[i].section(":",1,1)) ){ av[i] = av[i].section(":",2,2); }
+ else{ av.removeAt(i); i--; }*/
+ }
+ av.removeDuplicates();
+ return av;
+}
+
+QStringList LXDG::loadMimeFileGlobs2(){
+ //output format: <weight>:<mime type>:<file extension (*.something)>
+ if(mimeglobs.isEmpty() || (mimechecktime < (QDateTime::currentMSecsSinceEpoch()-30000)) ){
+ //qDebug() << "Loading globs2 mime DB files";
+ mimeglobs.clear();
+ mimechecktime = QDateTime::currentMSecsSinceEpoch(); //save the current time this was last checked
+ QStringList dirs = LXDG::systemMimeDirs();
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/globs2")){
+ QFile file(dirs[i]+"/globs2");
+ if(!file.exists()){ continue; }
+ if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ continue; }
+ QTextStream in(&file);
+ while(!in.atEnd()){
+ QString line = in.readLine();
+ if(!line.startsWith("#")){
+ mimeglobs << line.simplified();
+ }
+ }
+ file.close();
+ }
+ }
+ }
+ return mimeglobs;
+}
+
+//Find all the autostart *.desktop files
+QList<XDGDesktop> LXDG::findAutoStartFiles(bool includeInvalid){
+
+ //First get the list of directories to search (system first, user-provided files come later and overwrite sys files as needed)
+ QStringList paths = QString(getenv("XDG_CONFIG_DIRS")).split(":");
+ paths << QString(getenv("XDG_CONFIG_HOME")).split(":");
+ //Now go through them and find any valid *.desktop files
+ QList<XDGDesktop> files;
+ QStringList filenames; //make it easy to see if this filename is an override
+ QDir dir;
+ for(int i=0;i<paths.length(); i++){
+ if(!QFile::exists(paths[i]+"/autostart")){ continue; }
+ dir.cd(paths[i]+"/autostart");
+ QStringList tmp = dir.entryList(QStringList() << "*.desktop", QDir::Files, QDir::Name);
+ for(int t=0; t<tmp.length(); t++){
+ bool ok = false;
+ XDGDesktop desk = LXDG::loadDesktopFile(dir.absoluteFilePath(tmp[t]), ok);
+ if(!ok){ continue; } //could not read file
+ //Now figure out what to do with it
+ if(filenames.contains(tmp[t])){
+ //This is an overwrite of a lower-priority (system?) autostart file
+ // find the other file
+ int old = -1;
+ for(int o=0; o<files.length(); o++){
+ if(files[o].filePath.endsWith("/"+tmp[t])){ old = o; break; } //found it
+ }
+ if(LXDG::checkValidity(desk, false)){
+ //Full override of the lower-priority file (might be replacing exec/tryexec fields)
+ files[old] = desk;
+ }else{
+ //Small override file (only the "Hidden" field listed in spec)
+ files[old].isHidden = desk.isHidden; //replace this value with the override
+ //files << desk; //still add this to the array (will be ignored/skipped later)
+ }
+ }else{
+ //This is a new autostart file
+ files << desk;
+ filenames << tmp[t];
+ }
+ }//end of loop over *.desktop files
+ } //end of loop over directories
+
+ //Now filter the results by validity if desired
+ if(!includeInvalid){
+ for(int i=0; i<files.length(); i++){
+ if( !LXDG::checkValidity(files[i], false) || files[i].isHidden ){
+ //Invalid file - go ahead and remove it from the output list
+ files.removeAt(i);
+ i--;
+ }
+ }
+ }
+
+ return files;
+}
+
+bool LXDG::setAutoStarted(bool autostart, XDGDesktop app){
+ //First get the list of system directories to search (system first, user-provided files come later and overwrite sys files as needed)
+ QStringList paths = QString(getenv("XDG_CONFIG_DIRS")).split(":");
+ QString upath = QString(getenv("XDG_CONFIG_HOME")).section(":",0,0);
+ if(upath.isEmpty()){ upath = QDir::homePath()+"/.config/autostart/"; }
+ else{ upath.append("/autostart/"); }
+ //Quick check/finish for user-defined files which are getting disabled (just remove the file)
+ if(app.filePath.startsWith(upath) && !autostart){
+ return QFile::remove(app.filePath);
+ }
+ bool sysfile = false;
+ for(int i=0; i<paths.length(); i++){
+ if(app.filePath.startsWith(paths[i]+"/autostart/") ){
+ sysfile = true;
+ //Change it to the user-modifiable directory
+ app.filePath = app.filePath.replace(paths[i]+"/autostart/", upath);
+ }
+ }
+ //Make sure the user-autostart dir is specified, and clean the app structure as necessary
+ if( !app.filePath.startsWith(upath) && autostart){
+ //Some other non-override autostart file - set it up to open with lumina-open
+ if(!app.filePath.endsWith(".desktop")){
+ app.exec = "lumina-open \""+app.filePath+"\"";
+ app.tryexec = app.filePath; //make sure this file exists
+ if(app.name.isEmpty()){ app.name = app.filePath.section("/",-1); }
+ if(app.icon.isEmpty()){ app.icon = LXDG::findAppMimeForFile(app.filePath); app.icon.replace("/","-"); }
+ app.filePath = upath+app.filePath.section("/",-1)+".desktop";
+ app.type = XDGDesktop::APP;
+ }else{
+ //Some other *.desktop file on the system (keep almost all the existing settings/values)
+ // - setup a redirect to the other file
+ app.exec = "lumina-open \""+app.filePath+"\"";
+ app.tryexec = app.filePath; //make sure this file exists
+ // - Adjust the actual path where this file will get saved
+ app.filePath = upath+app.filePath.section("/",-1);
+ }
+ }
+ //Now save the "hidden" value into the file
+ app.isHidden = !autostart; //if hidden, it will not be autostarted
+ //Now save the file as necessary
+ bool saved = false;
+ //qDebug() << " - Saving AutoStart File:" << app.filePath << app.name << app.isHidden;
+ if(sysfile){
+ //Just an override file for the "hidden" field - nothing more
+ QStringList info;
+ info << "[Desktop Entry]" << "Type=Application" << QString("Hidden=")+ (app.isHidden ? QString("true"): QString("false"));
+ saved = LUtils::writeFile(app.filePath, info, true);
+ }else{
+ //Need to actually save the full file
+ saved = LXDG::saveDesktopFile(app);
+ }
+ return saved;
+}
+
+bool LXDG::setAutoStarted(bool autostart, QString filePath){
+ //Convenience function for the auto-start setter
+ XDGDesktop desk;
+ if(filePath.endsWith(".desktop")){
+ bool ok = false;
+ desk = LXDG::loadDesktopFile(filePath, ok);
+ if(!ok){ return false; } //error reading input file
+ }else{
+ desk.filePath = filePath;
+ desk.useTerminal = false;
+ }
+ return LXDG::setAutoStarted(autostart, desk);
+}
diff --git a/src-qt5/core/libLumina/LuminaXDG.h b/src-qt5/core/libLumina/LuminaXDG.h
new file mode 100644
index 00000000..5a9b1441
--- /dev/null
+++ b/src-qt5/core/libLumina/LuminaXDG.h
@@ -0,0 +1,168 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2013, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// These structures/classes are for conforming to the FreeDesktop standards
+// REFERENCE: (*.desktop files) http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
+// -- Current Implementation (OCT 2013) --
+// Desktop File Version Compliance: 1.0 (except "DBusActivatable")
+// Icon Theme Compliance: Built in to Qt (QIcon::fromTheme()) with "oxygen" theme default
+// *.desktop Exec Compliance Updated: 9/9/2014
+// Mime Application Version Compliance: 1.0.1 (11/14/14) (Skips random *.desktop parsing: ~80% compliant)
+//===========================================
+
+
+#ifndef _LUMINA_LIBRARY_XDG_H
+#define _LUMINA_LIBRARY_XDG_H
+
+#include <QFile>
+#include <QDir>
+#include <QFileInfo>
+#include <QStringList>
+#include <QString>
+#include <QIcon>
+#include <QList>
+#include <QHash>
+#include <QLocale>
+#include <QTextStream>
+#include <QDateTime>
+#include <QDebug>
+
+
+// ======================
+// FreeDesktop Desktop Actions Framework (data structure)
+// ======================
+class XDGDesktopAction{
+public:
+ //Admin variables
+ QString ID; //The ID name for this action (should correspond to an entry in the "actionList" for the XDGDesktop below)
+ //General Variables
+ QString name, icon, exec;
+};
+
+// ======================
+// FreeDesktop Desktop Entry Framework (data structure)
+// ======================
+class XDGDesktop{
+public:
+ enum XDGDesktopType { BAD, APP, LINK, DIR };
+ //Admin variables
+ QString filePath; //which file this structure contains the information for (absolute path)
+ QDateTime lastRead; //when this structure was created from the file
+ XDGDesktopType type;
+ //General variables
+ QString name, genericName, comment, icon;
+ QStringList showInList, notShowInList;
+ bool isHidden;
+ //Type 1 (APP) variables
+ QString exec, tryexec, path, startupWM;
+ QStringList actionList, mimeList, catList, keyList;
+ bool useTerminal, startupNotify;
+ QList<XDGDesktopAction> actions;
+ //Type 2 (LINK) variables
+ QString url;
+
+ //Constructor/destructor
+ XDGDesktop(){}
+ ~XDGDesktop(){}
+};
+
+// ========================
+// File Information simplification class (combine QFileInfo with XDGDesktop)
+// Need some extra information not usually available by a QFileInfo
+// ========================
+class LFileInfo : public QFileInfo{
+private:
+ QString mime, icon;
+ XDGDesktop desk;
+
+ void loadExtraInfo();
+
+public:
+ //Couple overloaded contructors
+ LFileInfo(QString filepath);
+ LFileInfo(QFileInfo info);
+ ~LFileInfo(){}
+
+ //Functions for accessing the extra information
+ // -- Return the mimetype for the file
+ QString mimetype();
+
+ // -- Return the icon file to use for this file
+ QString iconfile(); //Note: This string is auto-formatted for use in the LXDG::findIcon() routine.
+
+ // -- Check if this is an XDG desktop file
+ bool isDesktopFile();
+
+ // -- Allow access to the internal XDG desktop data structure
+ XDGDesktop* XDG();
+
+ //Other file type identification routines
+ bool isImage(); //Is a readable image file (for thumbnail support)
+ bool isAVFile(); //Is an audio/video file
+};
+typedef QList<LFileInfo> LFileInfoList;
+
+// ================================
+// Collection of FreeDesktop standards interaction routines
+// ================================
+class LXDG{
+public:
+ //Read/write a *.desktop file
+ static XDGDesktop loadDesktopFile(QString filePath, bool& ok);
+ static bool saveDesktopFile(XDGDesktop dFile, bool merge = true);
+ //Check a *.desktop file for validity (showAll skips the DE-exclusivity checks)
+ static bool checkValidity(XDGDesktop dFile, bool showAll = true);
+ //Check for a valid executable
+ static bool checkExec(QString exec);
+ //Get a list of all the directories where *.desktop files exist
+ static QStringList systemApplicationDirs();
+ //Get a list of all the *.desktop files available on the system
+ static QList<XDGDesktop> systemDesktopFiles(bool showAll = false, bool showHidden = false);
+ //Sort a list of Desktop files into the proper categories
+ static QHash< QString, QList<XDGDesktop> > sortDesktopCats(QList<XDGDesktop> apps);
+ //Return the icon to use for the given category
+ static QString DesktopCatToIcon(QString cat);
+ //Sort a list of Desktop files by name
+ static QList<XDGDesktop> sortDesktopNames(QList<XDGDesktop> apps);
+ //Get the executable line from a Desktop file
+ static QString getDesktopExec(XDGDesktop app, QString ActionID = "");
+ //Set all the default XDG Environment variables
+ static void setEnvironmentVars();
+ //Find an icon from the current/default theme
+ static QIcon findIcon(QString iconName, QString fallback = "");
+ //Recursivly compile a list of child directories with *.png files in them
+ static QStringList getChildIconDirs(QString parent);
+ //List all the mime-type directories
+ static QStringList systemMimeDirs();
+ //Find the mime-type icon for a particular file extension
+ static QIcon findMimeIcon(QString extension);
+ //Find the mime-type of a particular file extension
+ static QString findAppMimeForFile(QString filename, bool multiple = false);
+ //Find the file extension for a particular mime-type
+ static QStringList findFilesForMime(QString mime);
+ // Simplification function for finding all info regarding current mime defaults
+ static QStringList listFileMimeDefaults();
+ //Find the localized comment string for a particular mime-type
+ static QString findMimeComment(QString mime);
+ //Find the default application for a mime-type
+ static QString findDefaultAppForMime(QString mime);
+ //Fine the available applications for a mime-type
+ static QStringList findAvailableAppsForMime(QString mime);
+ //Set the default application for a mime-type
+ static void setDefaultAppForMime(QString mime, QString app);
+ //List all the registered audio/video file extensions
+ static QStringList findAVFileExtensions();
+ //Load all the "globs2" mime database files
+ static QStringList loadMimeFileGlobs2();
+
+ //Find all the autostart *.desktop files
+ static QList<XDGDesktop> findAutoStartFiles(bool includeInvalid = false);
+ static bool setAutoStarted(bool autostart, XDGDesktop app);
+ static bool setAutoStarted(bool autostart, QString filePath); //for convenience
+};
+
+#endif
+
diff --git a/src-qt5/core/libLumina/colors/Black.qss.colors b/src-qt5/core/libLumina/colors/Black.qss.colors
new file mode 100644
index 00000000..b6269188
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Black.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(204,204,204,200)
+ACCENTDISABLECOLOR=rgba(204,204,204,100)
+ALTBASECOLOR=rgb(37,37,37)
+BASECOLOR=rgb(36,36,36)
+HIGHLIGHTCOLOR=rgba(218,222,226,170)
+HIGHLIGHTDISABLECOLOR=rgba(218,222,226,160)
+PRIMARYCOLOR=rgba(0,0,4,250)
+PRIMARYDISABLECOLOR=rgba(0,0,0,180)
+SECONDARYCOLOR=rgba(192,192,192,200)
+SECONDARYDISABLECOLOR=rgba(192,192,192,100)
+TEXTCOLOR=white
+TEXTDISABLECOLOR=darkgrey
+TEXTHIGHLIGHTCOLOR=black
diff --git a/src-qt5/core/libLumina/colors/Blue-Light.qss.colors b/src-qt5/core/libLumina/colors/Blue-Light.qss.colors
new file mode 100644
index 00000000..5bcb85f6
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Blue-Light.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(157,161,165,200)
+ACCENTDISABLECOLOR=rgba(156,156,157,200)
+ALTBASECOLOR=rgb(239,247,255)
+BASECOLOR=rgb(165,210,255)
+HIGHLIGHTCOLOR=rgb(110,158,208)
+HIGHLIGHTDISABLECOLOR=rgba(110,158,208,150)
+PRIMARYCOLOR=rgba(181,212,238,200)
+PRIMARYDISABLECOLOR=rgba(234,237,238,100)
+SECONDARYCOLOR=rgba(200,222,243,200)
+SECONDARYDISABLECOLOR=rgba(200,222,243,100)
+TEXTCOLOR=black
+TEXTDISABLECOLOR=grey
+TEXTHIGHLIGHTCOLOR=black
diff --git a/src-qt5/core/libLumina/colors/Grey-Dark.qss.colors b/src-qt5/core/libLumina/colors/Grey-Dark.qss.colors
new file mode 100644
index 00000000..207edd04
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Grey-Dark.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(43,43,45,200)
+ACCENTDISABLECOLOR=rgba(43,43,45,100)
+ALTBASECOLOR=rgb(63,61,61)
+BASECOLOR=rgb(93,92,92)
+HIGHLIGHTCOLOR=rgba(218,222,226,170)
+HIGHLIGHTDISABLECOLOR=rgba(218,222,226,160)
+PRIMARYCOLOR=rgba(75,77,80,240)
+PRIMARYDISABLECOLOR=rgba(75,77,80,180)
+SECONDARYCOLOR=rgba(144,140,142,200)
+SECONDARYDISABLECOLOR=rgba(144,140,142,100)
+TEXTCOLOR=white
+TEXTDISABLECOLOR=darkgrey
+TEXTHIGHLIGHTCOLOR=black
diff --git a/src-qt5/core/libLumina/colors/Lumina-Glass.qss.colors b/src-qt5/core/libLumina/colors/Lumina-Glass.qss.colors
new file mode 100644
index 00000000..7b74e036
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Lumina-Glass.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(255,252,234,100)
+ACCENTDISABLECOLOR=rgba(0,0,0,100)
+ALTBASECOLOR=rgb(252,252,255)
+BASECOLOR=rgb(247,246,244)
+HIGHLIGHTCOLOR=rgba(212,212,212,170)
+HIGHLIGHTDISABLECOLOR=rgba(184,184,184,100)
+PRIMARYCOLOR=rgba(235,242,242,200)
+PRIMARYDISABLECOLOR=rgba(214,220,220,200)
+SECONDARYCOLOR=rgba(208,220,244,200)
+SECONDARYDISABLECOLOR=rgba(168,179,200,100)
+TEXTCOLOR=black
+TEXTDISABLECOLOR=grey
+TEXTHIGHLIGHTCOLOR=black \ No newline at end of file
diff --git a/src-qt5/core/libLumina/colors/Lumina-Gold.qss.colors b/src-qt5/core/libLumina/colors/Lumina-Gold.qss.colors
new file mode 100644
index 00000000..cfad7069
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Lumina-Gold.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(149,144,122,200)
+ACCENTDISABLECOLOR=rgba(74,71,60,200)
+ALTBASECOLOR=rgb(230,227,204)
+BASECOLOR=rgb(247,247,240)
+HIGHLIGHTCOLOR=rgba(252,237,149,170)
+HIGHLIGHTDISABLECOLOR=rgba(252,237,149,100)
+PRIMARYCOLOR=rgba(238,234,226,200)
+PRIMARYDISABLECOLOR=rgba(238,235,224,100)
+SECONDARYCOLOR=rgba(247,209,112,200)
+SECONDARYDISABLECOLOR=rgba(234,198,106,150)
+TEXTCOLOR=black
+TEXTDISABLECOLOR=grey
+TEXTHIGHLIGHTCOLOR=black \ No newline at end of file
diff --git a/src-qt5/core/libLumina/colors/Lumina-Green.qss.colors b/src-qt5/core/libLumina/colors/Lumina-Green.qss.colors
new file mode 100644
index 00000000..99f16acb
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Lumina-Green.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(149,144,122,200)
+ACCENTDISABLECOLOR=rgba(74,71,60,200)
+ALTBASECOLOR=rgb(230,230,230)
+BASECOLOR=rgb(247,246,244)
+HIGHLIGHTCOLOR=rgba(66,153,76,170)
+HIGHLIGHTDISABLECOLOR=rgba(66,153,76,100)
+PRIMARYCOLOR=rgba(229,231,238,200)
+PRIMARYDISABLECOLOR=rgba(229,231,238,100)
+SECONDARYCOLOR=rgba(76,197,84,200)
+SECONDARYDISABLECOLOR=rgba(76,197,84,150)
+TEXTCOLOR=black
+TEXTDISABLECOLOR=grey
+TEXTHIGHLIGHTCOLOR=black
diff --git a/src-qt5/core/libLumina/colors/Lumina-Purple.qss.colors b/src-qt5/core/libLumina/colors/Lumina-Purple.qss.colors
new file mode 100644
index 00000000..f2ba7e05
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Lumina-Purple.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(247,231,255,200)
+ACCENTDISABLECOLOR=rgba(167,166,166,200)
+ALTBASECOLOR=rgb(63,61,61)
+BASECOLOR=rgb(93,92,92)
+HIGHLIGHTCOLOR=rgba(76,38,171,170)
+HIGHLIGHTDISABLECOLOR=rgba(57,19,139,170)
+PRIMARYCOLOR=rgba(65,67,80,240)
+PRIMARYDISABLECOLOR=rgba(65,67,80,180)
+SECONDARYCOLOR=rgba(74,65,120,200)
+SECONDARYDISABLECOLOR=rgba(71,65,120,100)
+TEXTCOLOR=white
+TEXTDISABLECOLOR=darkgrey
+TEXTHIGHLIGHTCOLOR=white \ No newline at end of file
diff --git a/src-qt5/core/libLumina/colors/Lumina-Red.qss.colors b/src-qt5/core/libLumina/colors/Lumina-Red.qss.colors
new file mode 100644
index 00000000..f73bdb75
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Lumina-Red.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(255,244,245,200)
+ACCENTDISABLECOLOR=rgba(167,166,166,200)
+ALTBASECOLOR=rgb(63,61,61)
+BASECOLOR=rgb(93,92,92)
+HIGHLIGHTCOLOR=rgba(175,9,9,170)
+HIGHLIGHTDISABLECOLOR=rgba(154,20,20,170)
+PRIMARYCOLOR=rgba(80,66,66,240)
+PRIMARYDISABLECOLOR=rgba(80,66,66,180)
+SECONDARYCOLOR=rgba(120,22,23,200)
+SECONDARYDISABLECOLOR=rgba(120,22,23,100)
+TEXTCOLOR=white
+TEXTDISABLECOLOR=darkgrey
+TEXTHIGHLIGHTCOLOR=white \ No newline at end of file
diff --git a/src-qt5/core/libLumina/colors/PCBSD10-Default.qss.colors b/src-qt5/core/libLumina/colors/PCBSD10-Default.qss.colors
new file mode 100644
index 00000000..efcea51d
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/PCBSD10-Default.qss.colors
@@ -0,0 +1,13 @@
+ACCENTCOLOR=rgba(182,186,191,200)
+ACCENTDISABLECOLOR=rgba(190,190,191,200)
+ALTBASECOLOR=rgb(241,241,241)
+BASECOLOR=rgb(247,246,244)
+HIGHLIGHTCOLOR=rgb(129,184,243)
+HIGHLIGHTDISABLECOLOR=rgba(129,184,243,150)
+PRIMARYCOLOR=rgba(224,236,238,200)
+PRIMARYDISABLECOLOR=rgba(234,237,238,100)
+SECONDARYCOLOR=rgba(200,222,243,200)
+SECONDARYDISABLECOLOR=rgba(200,222,243,100)
+TEXTCOLOR=black
+TEXTDISABLECOLOR=grey
+TEXTHIGHLIGHTCOLOR=black
diff --git a/src-qt5/core/libLumina/colors/Solarized-Dark.qss.colors b/src-qt5/core/libLumina/colors/Solarized-Dark.qss.colors
new file mode 100644
index 00000000..d4f0f1c9
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Solarized-Dark.qss.colors
@@ -0,0 +1,16 @@
+# Solarized is a theme created by Ethan Schoonover
+# See the project site at http://ethanschoonover.com/solarized
+# Or see the source at https://github.com/altercation/solarized
+ACCENTCOLOR=rgb(181,137,0)
+ACCENTDISABLECOLOR=rgb(181,137,0)
+ALTBASECOLOR=rgb(0,43,54)
+BASECOLOR=rgb(0,43,54)
+HIGHLIGHTCOLOR=rgb(7,54,66)
+HIGHLIGHTDISABLECOLOR=rgb(7,53,66)
+PRIMARYCOLOR=rgb(0,43,54)
+PRIMARYDISABLECOLOR=rgb(7,54,66)
+SECONDARYCOLOR=rgb(0,43,54)
+SECONDARYDISABLECOLOR=rgb(7,54,66)
+TEXTCOLOR=rgb(131,148,150)
+TEXTDISABLECOLOR=rgb(88,110,117)
+TEXTHIGHLIGHTCOLOR=rgb(147,161,161)
diff --git a/src-qt5/core/libLumina/colors/Solarized-Light.qss.colors b/src-qt5/core/libLumina/colors/Solarized-Light.qss.colors
new file mode 100644
index 00000000..fead1915
--- /dev/null
+++ b/src-qt5/core/libLumina/colors/Solarized-Light.qss.colors
@@ -0,0 +1,16 @@
+# Solarized is a theme created by Ethan Schoonover
+# See the project site at http://ethanschoonover.com/solarized
+# Or see the source at https://github.com/altercation/solarized
+ACCENTCOLOR=rgb(38,139,210)
+ACCENTDISABLECOLOR=rgb(38,139,210)
+ALTBASECOLOR=rgb(253,246,227)
+BASECOLOR=rgb(253,246,227)
+HIGHLIGHTCOLOR=rgb(238,232,213)
+HIGHLIGHTDISABLECOLOR=rgb(238,232,213)
+PRIMARYCOLOR=rgb(253,246,227)
+PRIMARYDISABLECOLOR=rgb(238,232,213)
+SECONDARYCOLOR=rgb(253,246,227)
+SECONDARYDISABLECOLOR=rgb(238,232,213)
+TEXTCOLOR=rgb(131,148,150)
+TEXTDISABLECOLOR=rgb(147,161,161)
+TEXTHIGHLIGHTCOLOR=rgb(88,110,117)
diff --git a/src-qt5/core/libLumina/libLumina.pro b/src-qt5/core/libLumina/libLumina.pro
new file mode 100644
index 00000000..63c56824
--- /dev/null
+++ b/src-qt5/core/libLumina/libLumina.pro
@@ -0,0 +1,65 @@
+include("$${PWD}/../../OS-detect.pri")
+
+QT += core network widgets x11extras multimedia concurrent svg
+
+define
+#Setup any special defines (qmake -> C++)
+GIT_VERSION=$$system(git describe --always)
+!isEmpty(GIT_VERSION){
+ DEFINES += GIT_VERSION='"\\\"$${GIT_VERSION}\\\""'
+}
+DEFINES += BUILD_DATE='"\\\"$$system(date)\\\""'
+
+TARGET=LuminaUtils
+
+target.path = $${L_LIBDIR}
+
+DESTDIR= $$_PRO_FILE_PWD_/
+
+TEMPLATE = lib
+LANGUAGE = C++
+VERSION = 1
+
+HEADERS += LuminaXDG.h \
+ LuminaUtils.h \
+ LuminaX11.h \
+ LuminaThemes.h \
+ LuminaOS.h \
+ LuminaSingleApplication.h
+
+SOURCES += LuminaXDG.cpp \
+ LuminaUtils.cpp \
+ LuminaX11.cpp \
+ LuminaThemes.cpp \
+ LuminaSingleApplication.cpp
+
+# Also load the OS template as available for
+# LuminaOS support functions (or fall back to generic one)
+exists($${PWD}/LuminaOS-$${LINUX_DISTRO}.cpp){
+ SOURCES += LuminaOS-$${LINUX_DISTRO}.cpp
+}else:exists($${PWD}/LuminaOS-$${OS}.cpp){
+ SOURCES += LuminaOS-$${OS}.cpp
+}else{
+ SOURCES += LuminaOS-template.cpp
+}
+
+LIBS += -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damage -lxcb-util -lXdamage
+
+include.path=$${L_INCLUDEDIR}
+include.files=LuminaXDG.h \
+ LuminaUtils.h \
+ LuminaX11.h \
+ LuminaThemes.h \
+ LuminaOS.h \
+ LuminaSingleApplication.h
+
+colors.path=$${L_SHAREDIR}/Lumina-DE/colors
+colors.files=colors/*.qss.colors
+
+themes.path=$${L_SHAREDIR}/Lumina-DE/themes/
+themes.files=themes/*.qss.template
+
+#quickplugins.path=$${L_SHAREDIR}/Lumina-DE/quickplugins/
+#quickplugins.files=quickplugins/*
+
+INSTALLS += target include colors themes
diff --git a/src-qt5/core/libLumina/quickplugins/quick-sample.qml b/src-qt5/core/libLumina/quickplugins/quick-sample.qml
new file mode 100644
index 00000000..18b10d77
--- /dev/null
+++ b/src-qt5/core/libLumina/quickplugins/quick-sample.qml
@@ -0,0 +1,12 @@
+// Plugin-Name=Sample
+// Plugin-Description=A simple example for QtQuick/QML plugins
+// Plugin-Icon=preferences-plugin
+// Created: Ken Moore (ken@pcbsd.org) May 2015
+
+import QtQuick.Controls 1.3
+
+Label {
+ text: "Sample"
+ color: "blue"
+ font.bold: true
+} \ No newline at end of file
diff --git a/src-qt5/core/libLumina/themes/Lumina-default.qss.template b/src-qt5/core/libLumina/themes/Lumina-default.qss.template
new file mode 100644
index 00000000..a40a3d48
--- /dev/null
+++ b/src-qt5/core/libLumina/themes/Lumina-default.qss.template
@@ -0,0 +1,488 @@
+/* ALL THE TEMPLATE WIDGETS */
+INHERITS=None
+
+/* ALL THE WIDGETS WITH THE BASE COLOR */
+QMainWindow, QMenu, QDialog, QMessageBox{
+ background: %%BASECOLOR%%;
+ color: %%TEXTCOLOR%%;
+}
+
+/* ALL THE WIDGETS WITH AN ALTERNATE BASE COLOR */
+QLineEdit, QTextEdit, QTextBrowser, QPlainTextEdit, QSpinBox, QDateEdit, QDateTimeEdit, QTimeEdit, QDoubleSpinBox{
+ background: %%ALTBASECOLOR%%;
+ color: %%TEXTCOLOR%%;
+ border-color: %%ACCENTDISABLECOLOR%%;
+ selection-background-color: %%HIGHLIGHTCOLOR%%;
+ selection-color: %%TEXTHIGHLIGHTCOLOR%%;
+
+}
+
+/* PAGES OF CONTAINER WIDGETS */
+QStackedWidget .QWidget, QTabWidget .QWidget{
+ background: %%ALTBASECOLOR%%;
+ color: %%TEXTCOLOR%%;
+ border: none;
+}
+QToolBox::tab{
+ color: %%TEXTCOLOR%%;
+}
+
+/* MENU WIDGETS */
+QMenuBar, QMenuBar::item,QToolBar{
+ background: %%SECONDARYCOLOR%%;
+ border: none;
+ color: %%TEXTCOLOR%%;
+}
+
+QStatusBar{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 transparent, stop: 0.5 %%SECONDARYCOLOR%%);
+ border: none;
+ color: %%TEXTCOLOR%%;
+}
+QToolBar:top{
+ border-bottom: 1px solid %%ACCENTCOLOR%%;
+}
+QToolBar:bottom{
+ border-top: 1px solid %%ACCENTCOLOR%%;
+}
+QToolBar:left{
+ border-right: 1px solid %%ACCENTCOLOR%%;
+}
+QToolBar:right{
+ border-left: 1px solid %%ACCENTCOLOR%%;
+}
+
+QMenuBar::item{
+ background: transparent; /*Use the menu bar color*/
+ padding-left: 4px;
+ padding-right: 2px;
+}
+
+QMenuBar::item:selected, QMenuBar::item:pressed, QMenu::item:selected{
+background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 %%HIGHLIGHTCOLOR%%);
+color: %%TEXTHIGHLIGHTCOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+}
+QMenuBar::item:disabled{
+ color: %%TEXTDISABLECOLOR%%;
+}
+
+QMenu::item{
+ background: transparent;
+ border: 1px solid transparent;
+ color: %%TEXTCOLOR%%;
+ padding: 2px 30px 2px 20px;
+}
+
+/* TAB WIDGETS */
+/*QTabBar{
+ Custom Font settings need to be here and NOT in the ::tab fields,
+ otherwise it will break auto-scaling of the tab sizes to fit the text
+}*/
+QTabBar::tab {
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%SECONDARYDISABLECOLOR%%, stop: 1 %%SECONDARYCOLOR%%);
+ border: 1px solid %%ACCENTCOLOR%%;
+ padding: 2px;
+ color: %%TEXTCOLOR%%;
+}
+QTabBar::tab:top{
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ max-width: 100em;
+ min-width: 0em;
+}
+QTabBar::tab:bottom{
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ max-width: 100em;
+ min-width: 0em;
+}
+/* left/right tab indicators appear to be reversed in Qt*/
+QTabBar::tab:right{
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ max-height: 100em;
+ min-height: 0em;
+}
+QTabBar::tab:left{
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ max-height: 100em;
+ min-height: 0em;
+}
+QTabBar::tab:selected{
+ background: %%HIGHLIGHTDISABLECOLOR%%;
+}
+QTabBar::tab:hover {
+ background: %%HIGHLIGHTCOLOR%%;
+ border: 1px solid %%ACCENTDISABLECOLOR%%;
+ }
+
+QTabBar::tab:!selected:top {
+ margin-top: 4px;
+}
+QTabBar::tab:!selected:bottom{
+ margin-bottom: 4px;
+}
+QTabBar::tab:!selected:right{
+ margin-left: 4px;
+}
+QTabBar::tab:!selected:left{
+ margin-right: 4px;
+}
+
+/* FRAME WIDGETS */
+QToolTip{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%BASECOLOR%%, stop: 1 %%ALTBASECOLOR%%);
+ border-radius: 3px;
+ border: 1px solid %%ACCENTCOLOR%%;
+ padding: 1px;
+ color: %%TEXTCOLOR%%;
+}
+
+QLabel{
+ background: transparent;
+ border: none;
+ color: %%TEXTCOLOR%%;
+}
+QAbstractButton::disabled{
+ color: %%TEXTDISABLECOLOR%%;
+}
+
+/* GROUP BOX */
+QGroupBox{
+ background-color: transparent;
+ border-color: %%ACCENTCOLOR%%;
+ border-radius: 5px;
+ margin-top: 2ex;
+ font-weight: bold;
+}
+QGroupBox::title{
+ subcontrol-origin: margin;
+ subcontrol-position: top center;
+ padding: 0 3px;
+ background: transparent;
+ /*border: none;*/
+ color: %%TEXTCOLOR%%;
+}
+
+/* COMBO BOX */
+QComboBox{
+ /*border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+ padding: 1px 18px 1px 3px;*/
+ color: %%TEXTCOLOR%%;
+ background: %%ALTBASECOLOR%%;
+ selection-background-color: %%HIGHLIGHTCOLOR%%;
+ }
+
+
+/* VIEW WIDGETS */
+QTreeView, QListView{
+ background: %%ALTBASECOLOR%%;
+ alternate-background-color: %%BASECOLOR%%;
+ /*selection-background-color: %%SECONDARYCOLOR%%;*/
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+ /*show-decoration-selected: 1;*/
+ color: %%TEXTCOLOR%%;
+ selection-color: %%TEXTCOLOR%%;
+}
+
+QTreeView:focus, QListView:focus{
+ border: 1px solid %%HIGHLIGHTDISABLECOLOR%%;
+}
+
+/*
+QTreeView::item and QListView::item unneccessary:
+Already set though parentage and causes usage errors if set manually
+*/
+
+/*QTreeView::item:selected, QListView::item:selected{
+ background: %%SECONDARYDISABLECOLOR%%;
+ border-color: %%ACCENTCOLOR%%;
+ color: %%TEXTCOLOR%%;
+}*/
+
+QTreeView::item:hover, QListView::item:hover{
+ background: %%HIGHLIGHTDISABLECOLOR%%;
+}
+QTreeView::item:selected:hover, QListView::item:selected:hover{
+ background: %%HIGHLIGHTDISABLECOLOR%%;
+}
+QTreeView::item:selected, QListView::item:selected{
+ background: %%SECONDARYDISABLECOLOR%%;
+}
+QTreeView::item:selected:focus, QListView::item:selected:focus{
+ background: %%SECONDARYCOLOR%%;
+}
+QHeaderView{
+ background: %%HIGHLIGHTDISABLECOLOR%%;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+ border: none;
+ border-top-left-radius: 3px; /*match the list/tree view widgets*/
+ border-top-right-radius: 3px; /*match the list/tree view widgets*/
+}
+QHeaderView::section{
+ background: %%HIGHLIGHTDISABLECOLOR%%; /*QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%PRIMARYDISABLECOLOR%%, stop: 1 %%PRIMARYCOLOR%%);*/
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+ border-color: %%ACCENTCOLOR%%;
+ padding: 1px;
+ padding-left: 4px;
+}
+QHeaderView::section:hover{
+ background: %%PRIMARYCOLOR%%;
+ border-color: %%ACCENTDISABLECOLOR%%;
+ color: %%TEXTCOLOR%%;
+}
+
+/* SCROLLBARS (NOTE: Changing 1 subcontrol means you have to change all of them)*/
+QScrollBar{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 %%SECONDARYCOLOR%%, stop: 1 %%SECONDARYDISABLECOLOR%%);
+ /*border: 1px solid %%ACCENTCOLOR%%;*/
+}
+QScrollBar:horizontal{
+ margin: 0px 20px 0px 20px;
+}
+QScrollBar:vertical{
+ margin: 20px 0px 20px 0px;
+}
+QScrollBar::sub-page, QScrollBar::add-page{
+ background: %%BASECOLOR%%;
+ border: 1px solid %%ACCENTCOLOR%%;
+}
+QScrollBar::sub-page:vertical{
+ border-bottom: none;
+}
+QScrollBar::add-page:vertical{
+ border-top: none;
+}
+QScrollBar::sub-page:horizontal{
+ border-right: none;
+}
+QScrollBar::add-page:horizontal{
+ border-left: none;
+}
+QScrollBar::handle{
+ background: QLinearGradient(x1: 0, y1: -0.3, x2: 0, y2: 1.3, stop: 0 %%BASECOLOR%%, stop: 0.5 %%SECONDARYCOLOR%%, stop: 1 %%BASECOLOR%%);
+ border: 1px solid %%ACCENTCOLOR%%;
+}
+QScrollBar::handle:hover, QScrollBar::add-line:hover, QScrollBar::sub-line:hover{
+ background: %%HIGHLIGHTCOLOR%%;
+}
+QScrollBar::add-line{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 %%SECONDARYDISABLECOLOR%%, stop: 1 %%SECONDARYCOLOR%%);
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+subcontrol-position: bottom right;
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical{
+height: 20px;
+}
+QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal{
+width: 20px;
+}
+QScrollBar::sub-line{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 %%SECONDARYCOLOR%%, stop: 1 %%SECONDARYDISABLECOLOR%%);
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+subcontrol-position: top left;
+subcontrol-origin: margin;
+height: 20px;
+}
+/* SLIDERS */
+QSlider::groove:horizontal {
+border: 1px solid %%ACCENTCOLOR%%;
+background: %%ALTBASECOLOR%%;
+height: 10px;
+border-radius: 3px;
+}
+QSlider::groove:vertical {
+border: 1px solid %%ACCENTCOLOR%%;
+background: %%ALTBASECOLOR%%;
+width: 10px;
+border-radius: 3px;
+}
+QSlider::sub-page:horizontal {
+background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,
+ stop: 0 %%HIGHLIGHTCOLOR%%, stop: 1 %%HIGHLIGHTDISABLECOLOR%%);
+border: 1px solid %%ACCENTCOLOR%%;
+height: 10px;
+border-radius: 3px;
+}
+QSlider::sub-page:vertical {
+background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,
+ stop: 0 %%HIGHLIGHTCOLOR%%, stop: 1 %%HIGHLIGHTDISABLECOLOR%%);
+border: 1px solid %%ACCENTCOLOR%%;
+width: 10px;
+border-radius: 3px;
+}
+QSlider::add-page:horizontal{
+background: %%ALTBASECOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+height: 10px;
+border-radius: 3px;
+}
+QSlider::add-page:vertical{
+background: %%ALTBASECOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+width: 10px;
+border-radius: 3px;
+}
+QSlider::handle:horizontal{
+background: %%ALTBASECOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+width: 13px;
+border-radius: 4px;
+}
+QSlider::handle:vertical{
+background: %%ALTBASECOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+height: 13px;
+border-radius: 4px;
+}
+QSlider::handle:horizontal:hover, QSlider::handle:vertical:hover{
+border: 1px solid %%ACCENTDISABLECOLOR%%;
+/*background: %%HIGHLIGHTCOLOR%%;*/
+}
+
+QSlider::sub-page:horizontal:disabled {
+background: %%ACCENTDISABLECOLOR%%;
+border-color: %%ACCENTCOLOR%%;
+}
+
+QSlider::add-page:horizontal:disabled {
+background: %%ACCENTDISABLECOLOR%%;
+border-color: %%ACCENTCOLOR%%;
+}
+
+QSlider::handle:horizontal:disabled {
+background: %%ALTBASECOLOR%%;
+border: 1px solid %%ACCENTCOLOR%%;
+}
+
+/* BUTTONS */
+QPushButton{
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%SECONDARYDISABLECOLOR%%, stop: 1 %%SECONDARYCOLOR%%);
+ padding: 2px;
+ padding-right: 4px;
+ color: %%TEXTCOLOR%%;
+ }
+
+QToolButton{ /* Assume a flat button for every toolbutton by default*/
+ color: %%TEXTCOLOR%%;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ background-color: transparent;
+ padding: 1px;
+}
+
+ QPushButton:pressed, QPushButton:open, QPushButton:selected, QPushButton:checked, QPushButton:on, QToolButton:pressed, QToolButton:open, QToolButton:selected, QToolButton:checked, QToolButton:on{
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 %%HIGHLIGHTCOLOR%%);
+ margin-top: 2px;
+ }
+
+QPushButton:flat, QToolButton:flat{
+ background-color: transparent;
+ border: 1px solid transparent; /* no border for a flat button */
+}
+
+QPushButton:hover, QToolButton:hover{
+ border: 1px solid %%ACCENTCOLOR%%;
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 %%HIGHLIGHTCOLOR%%);
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+QRadioButton, QCheckBox{
+ padding: 2px;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ color: %%TEXTCOLOR%%;
+}
+QRadioButton::hover, QCheckBox:hover{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 %%HIGHLIGHTCOLOR%%);
+ border: 1px solid %%ACCENTCOLOR%%;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+QRadioButton::indicator, QCheckBox::indicator, QGroupBox::indicator{
+ border: 1px solid %%TEXTCOLOR%%;
+}
+QRadioButton::indicator{
+ border-radius: 7px;
+}
+QRadioButton::indicator:checked{
+ background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:1, fx:0.5, fy:0.5, stop:0 %%TEXTCOLOR%%, stop:0.25 %%TEXTCOLOR%%, stop:0.25001 transparent);
+}
+QCheckBox::indicator:checked, QGroupBox::indicator:checked{
+ padding: 1px;
+ background-origin: content;
+ background-clip: content;
+ background: %%TEXTCOLOR%%;
+}
+QCheckBox::indicator:indeterminate, QGroupBox::indicator:indeterminate{
+ padding: 1px;
+ background-origin: content;
+ background-clip: content;
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0.49 transparent, stop: 0.5 %%TEXTCOLOR%%);
+}
+
+/* PROGRESSBAR */
+QProgressBar{
+ background-color: %%ALTBASECOLOR%%;
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 5px;
+ color: %%TEXTCOLOR%%;
+ text-align: center;
+ padding: 1px;
+}
+ QProgressBar::chunk {
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%HIGHLIGHTCOLOR%%, stop: 1 %%HIGHLIGHTDISABLECOLOR%%);
+ /*border: 1px solid %%ACCENTDISABLECOLOR%%;*/
+ border-radius: 5px;
+ }
+QProgressBar::chunk:vertical{
+ margin-left: 2px;
+ margin-right: 2px;
+}
+QProgressBar::chunk:horizontal{
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+ /* SPINBOX */
+/*QAbstractSpinBox{
+ background-color: %%ALTBASECOLOR%%;
+ border: 1px solid %%ACCENTCOLOR%%;
+ border-radius: 3px;
+}
+QAbstractSpinBox:disabled{
+ color: %%ACCENTDISABLECOLOR%%;
+}*/
+/*QAbstractSpinBox::down-button{
+ subcontrol-origin: border;
+ subcontrol-position: left;
+ width: 16px;
+ border-width: 1px;
+}
+QAbstractSpinBox::up-button{
+ subcontrol-origin: border;
+ subcontrol-position: right;
+ width: 16px;
+ border-width: 1px;
+}*/
+/*
+QAbstractSpinBox::down-arrow{
+ border-image: url(":/trolltech/styles/commonstyle/images/left-16.png");
+ width: 16px;
+ height: 16px;
+}
+QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::down-arrow:off, QAbstractSpinBox::up-arrow:off{
+ border-image: url(:/none);
+}
+QAbstractSpinBox::up-arrow{
+ border-image: url(":/trolltech/styles/commonstyle/images/right-16.png");
+ width: 16px;
+ height: 16px;
+}*/
diff --git a/src-qt5/core/libLumina/themes/None.qss.template b/src-qt5/core/libLumina/themes/None.qss.template
new file mode 100644
index 00000000..7d923b1e
--- /dev/null
+++ b/src-qt5/core/libLumina/themes/None.qss.template
@@ -0,0 +1,118 @@
+/* This is a blank stylesheet to disable the Lumina Themes almost entirely*/
+QWidget{
+ font-family: %%FONT%%;
+ font-size: %%FONTSIZE%%;
+}
+/* Set the panel appearance for this theme (unless manually customized) */
+QWidget#LuminaPanelColor{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 %%PRIMARYCOLOR%%, stop: 1 %%PRIMARYDISABLECOLOR%%);
+ border-radius: 5px;
+}
+QWidget#LuminaBootSplash{
+ background: %%BASECOLOR%%;
+ border-radius: 5px;
+}
+
+/* Set the default canvas appearance for Lumina desktop plugins*/
+/* Default to a non-transparent background for all desktop plugins*/
+LDPlugin{
+ background: %%BASECOLOR%%;
+ border-radius: 5px;
+}
+/* Now specify which plugins should have a transparent background */
+LDPlugin#applauncher, LDPlugin#desktopview{
+ background: transparent;
+ border-radius: 5px;
+}
+
+LDPlugin#applauncher QToolButton{
+background: transparent;
+ border: none;
+ border-radius: 5px;
+ color: white;
+}
+LDPlugin#applauncher QToolButton:hover{
+ background: %%PRIMARYDISABLECOLOR%%;
+ border: none;
+ border-radius: 5px;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+
+LDPlugin#desktopview QListWidget{
+ background: transparent;
+ border: 1px solid transparent;
+}
+LDPlugin#desktopview QListWidget::item{
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
+ stop: 0 transparent,
+ stop: 0.7 transparent,
+ stop: 1.0 %%PRIMARYDISABLECOLOR%%);
+ border-radius: 5px;
+ color: %%TEXTCOLOR%%;
+}
+
+LDPlugin#desktopview QListWidget::item:hover{
+ background: %%PRIMARYDISABLECOLOR%%;
+ border-radius: 5px;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+/*For the special widgets on the user button*/
+UserItemWidget, ItemWidget{
+ background: transparent;
+ border-radius: 3px;
+}
+UserItemWidget:hover, ItemWidget:hover{
+ background: %%HIGHLIGHTCOLOR%%;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+
+/*Special taskmanager window buttons: based on window state*/
+LTBWidget{
+ border: 1px solid transparent;
+ border-radius: 3px;
+}
+LTBWidget::menu-indicator{ image: none; } /*disable the menu arrow*/
+LTBWidget#WindowVisible{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1.1, stop: 0.3 %%SECONDARYCOLOR%%, stop: 1 transparent);
+}
+LTBWidget#WindowInvisible{
+ /* Primary color is used for the panel appearance, so use that to make it disappear*/
+ background: transparent;
+}
+LTBWidget#WindowActive{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1.1, stop: 0.3 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 transparent);
+}
+LTBWidget#WindowAttention{
+ background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 1.1, stop: 0.3 %%HIGHLIGHTCOLOR%%, stop: 1 transparent);
+}
+LTBWidget:hover, LTBWidget#WindowVisible:hover, LTBWidget#WindowInvisible:hover, LTBWidget#WindowActive:hover, LTBWidget#WindowAttention:hover{
+ background: %%HIGHLIGHTCOLOR%%;
+ color: %%TEXTHIGHLIGHTCOLOR%%;
+ border: 1px solid %%ACCENTCOLOR%%;
+}
+
+/* CALENDER WIDGET */
+ /* (This is a special hack since there is no official support for stylesheets for this widget) */
+ QCalendarWidget QWidget#qt_calendar_navigationbar{
+ background-color: %%ALTBASECOLOR%%;
+ }
+QCalendarWidget QWidget{
+ background-color: %%BASECOLOR%%;
+ alternate-background-color: %%HIGHLIGHTDISABLECOLOR%%;
+ color: %%TEXTCOLOR%%;
+}
+QCalendarWidget QAbstractButton{
+ background-color: transparent;
+}
+QCalendarWidget QAbstractButton::menu-indicator{
+ image: none;
+}
+QCalendarWidget QAbstractItemView{
+ background-color: %%SECONDARYCOLOR%%;
+ selection-background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 %%HIGHLIGHTDISABLECOLOR%%, stop: 1 %%HIGHLIGHTCOLOR%%);;
+ selection-color: %%TEXTHIGHLIGHTCOLOR%%;
+}
+QCalendarWidget QWidget#qt_calendar_calendarview{
+ background-color: %%ALTBASECOLOR%%;
+ border: none;
+} \ No newline at end of file
bgstack15