forked from Shinonome/dots-hyprland
254 lines
7.8 KiB
C++
Executable File
254 lines
7.8 KiB
C++
Executable File
#include <pwd.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "nlohmann/json.hpp"
|
|
using namespace std;
|
|
using json = nlohmann::json;
|
|
|
|
// A simple struct to store the name and exec properties of a desktop entry
|
|
struct DesktopEntry {
|
|
string name;
|
|
string exec;
|
|
string icon;
|
|
string filename;
|
|
string filepath;
|
|
bool show;
|
|
};
|
|
|
|
string username;
|
|
vector<DesktopEntry> allApps;
|
|
json apps;
|
|
int mode = 0; // 0: Object, 1: Array, 2: Start (Contains JSON for letters)
|
|
string iconTheme = "";
|
|
|
|
// Returns the file name from a path
|
|
std::string getFileName(const std::string& path) {
|
|
// Find the last position of '/' or '\' in the path
|
|
size_t pos = path.find_last_of("/\\");
|
|
// If none is found, return the whole path
|
|
if (pos == std::string::npos) return path;
|
|
// Otherwise, return the substring after the last slash
|
|
return path.substr(pos + 1);
|
|
}
|
|
|
|
// Returns the file name without extension from a path
|
|
string getFileNameNoExt(const string& path) {
|
|
string filename = getFileName(path); // Get file name (with extension)
|
|
size_t pos = filename.find_last_of(".");
|
|
if (pos == string::npos) return filename; // Name found
|
|
return filename.substr(0, pos);
|
|
}
|
|
|
|
// Function to check if file exists, if yes read it
|
|
string readIfExists(const string& name) {
|
|
ifstream f(name.c_str());
|
|
stringstream buffer;
|
|
if (f) { // check if the file was opened successfully
|
|
buffer << f.rdbuf(); // read the whole file into a string stream
|
|
f.close(); // close the file when done
|
|
}
|
|
return buffer.str(); // return the string stream as a string
|
|
}
|
|
|
|
void writeToFile(const string& name, const string& content) {
|
|
ofstream f(name.c_str(), ios::app); // open the file in append mode
|
|
if (f) { // check if the file was opened successfully
|
|
f << content << "\n"; // write the content to the file
|
|
f.close(); // close the file when done
|
|
} else {
|
|
cerr << "Error: could not open " << name
|
|
<< "\n"; // print an error message to the standard error
|
|
}
|
|
}
|
|
|
|
string exec(const char* cmd) {
|
|
array<char, 128> buffer;
|
|
string result;
|
|
unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
|
|
if (!pipe) {
|
|
throw runtime_error("popen() failed!");
|
|
}
|
|
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
|
|
result += buffer.data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
string getIconPath(string iconname) {
|
|
if (iconTheme == "") {
|
|
iconTheme =
|
|
exec(string("gsettings get org.gnome.desktop.interface icon-theme")
|
|
.c_str());
|
|
iconTheme.pop_back();
|
|
// cout << "icon theme: " << iconTheme << '\n';
|
|
}
|
|
if (iconname.size() == 0) {
|
|
return "";
|
|
} else if (iconname[0] == '/') {
|
|
return iconname; // Already absolute path
|
|
} else if (iconname[0] == '\n') {
|
|
return ""; // wtf
|
|
}
|
|
string path = readIfExists("/home/" + username + "/.config/eww/scripts/cache/" + iconname);
|
|
if (path == "") {
|
|
path = exec(string("geticons -t " + iconTheme + " " + string(iconname) +
|
|
" | head -n 1")
|
|
.c_str());
|
|
// cout << "path: " << path << '\n';
|
|
writeToFile("/home/" + username + "/.config/eww/scripts/cache/" + iconname, path);
|
|
// cout << "icon name: " << iconname << '\n';
|
|
// cout << "path: " << path << '\n';
|
|
}
|
|
while (path.size() > 0 && *path.rbegin() == '\n')
|
|
path.pop_back(); // Remove '\n'
|
|
return path;
|
|
}
|
|
|
|
// A function that reads a .desktop file and returns a DesktopEntry struct
|
|
DesktopEntry readDesktopFile(const string& filename) {
|
|
DesktopEntry entry;
|
|
entry.show = true;
|
|
entry.filename = getFileNameNoExt(filename);
|
|
entry.filepath = filename;
|
|
|
|
ifstream file(filename);
|
|
if (file.is_open()) {
|
|
string line;
|
|
while (getline(file, line)) {
|
|
// Skip comments and empty lines
|
|
if (line.empty() || line[0] == '#') {
|
|
continue;
|
|
}
|
|
if (line.substr(0, 1) == "[" &&
|
|
line.substr(0, 15) == "[Desktop Action")
|
|
break;
|
|
// Split the line by '=' and store the key-value pair
|
|
size_t pos = line.find('=');
|
|
if (pos != string::npos) {
|
|
string key = line.substr(0, pos);
|
|
string value = line.substr(pos + 1);
|
|
// Store properties
|
|
if (key == "Name") {
|
|
entry.name = value;
|
|
} else if (key == "Exec") {
|
|
entry.exec = value;
|
|
} else if (key == "Icon") {
|
|
entry.icon = getIconPath(value);
|
|
} else if (key == "NoDisplay" && value == "true") {
|
|
entry.show = false;
|
|
}
|
|
}
|
|
}
|
|
file.close();
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
string lowercaseOf(string s) {
|
|
for (char& c : s) {
|
|
c = tolower(c);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
bool lf(DesktopEntry a, DesktopEntry b) {
|
|
if (tolower(a.name[0]) == tolower(b.name[0]))
|
|
return a.name < b.name;
|
|
else
|
|
return tolower(a.name[0]) < tolower(b.name[0]);
|
|
}
|
|
|
|
// A function that prints out all desktop entry names and exec properties in a
|
|
// given directory
|
|
void getDesktopEntries(const string& dirname) {
|
|
// Iterate over all files in the directory
|
|
for (const auto& entry : filesystem::directory_iterator(dirname)) {
|
|
// Check if the file has a .desktop extension
|
|
if (entry.path().extension() == ".desktop") {
|
|
// Read the file and print its name and exec properties
|
|
DesktopEntry thisEntry = readDesktopFile(entry.path());
|
|
// cout << thisEntry.name << " [icon: " << thisEntry.icon << "]\n";
|
|
if (thisEntry.show) allApps.push_back(thisEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void toJson() {
|
|
sort(allApps.begin(), allApps.end(), lf);
|
|
int i = -1;
|
|
for (const auto& entry : allApps) {
|
|
i++;
|
|
// cout << entry.name << ", ";
|
|
json thisApp;
|
|
thisApp["name"] = entry.name;
|
|
thisApp["icon"] = entry.icon;
|
|
thisApp["exec"] = entry.exec;
|
|
if (mode != 2) {
|
|
thisApp["filename"] = entry.filename;
|
|
thisApp["filepath"] = entry.filepath;
|
|
}
|
|
// Get
|
|
if (mode == 0)
|
|
apps[entry.name] = thisApp;
|
|
else if (mode == 1)
|
|
apps.push_back(thisApp);
|
|
else if (mode == 2) {
|
|
apps.push_back(thisApp);
|
|
}
|
|
}
|
|
cout << apps << '\n';
|
|
}
|
|
|
|
string getUsername() {
|
|
uid_t uid = geteuid();
|
|
struct passwd* pw = getpwuid(uid);
|
|
return pw->pw_name;
|
|
}
|
|
|
|
void addLetters() {
|
|
for (char c = 'A'; c <= 'Z'; c++) {
|
|
DesktopEntry thisLetter;
|
|
thisLetter.name = c;
|
|
thisLetter.exec = "";
|
|
thisLetter.icon = "_letter";
|
|
allApps.push_back(thisLetter);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
ios::sync_with_stdio(false);
|
|
cin.tie(nullptr);
|
|
|
|
if (argc == 3 && string(argv[1]) == "--mode") {
|
|
if (string(argv[2]) == "object")
|
|
mode = 0;
|
|
else if (string(argv[2]) == "array")
|
|
mode = 1;
|
|
else if (string(argv[2]) == "start")
|
|
mode = 2;
|
|
else
|
|
mode = stoi(string(argv[2]));
|
|
}
|
|
|
|
username = getUsername();
|
|
// Print all desktop entries in common locations
|
|
string entryDirs[3] = {"/usr/share/applications/",
|
|
"/home/" + username + "/.local/share/applications",
|
|
"/var/lib/flatpak/exports/share/applications"};
|
|
for (string directory : entryDirs) {
|
|
if (filesystem::exists(directory))
|
|
getDesktopEntries(directory);
|
|
}
|
|
if (mode == 2) addLetters();
|
|
// Get a json and print
|
|
toJson();
|
|
} |