|
| 1 | +#include <iostream> |
| 2 | +#include <fstream> |
| 3 | +#include <string> |
| 4 | +#include <vector> |
| 5 | +#include <regex> |
| 6 | +#include <cstdlib> |
| 7 | +#include <sstream> |
| 8 | +#include <iomanip> |
| 9 | + |
| 10 | +std::string trim(const std::string& str) { |
| 11 | + size_t first = str.find_first_not_of(" \t"); |
| 12 | + if (std::string::npos == first) return ""; |
| 13 | + size_t last = str.find_last_not_of(" \t"); |
| 14 | + return str.substr(first, (last - first + 1)); |
| 15 | +} |
| 16 | + |
| 17 | +void replace_all(std::string& str, const std::string& from, const std::string& to) { |
| 18 | + if (from.empty()) return; |
| 19 | + size_t start_pos = 0; |
| 20 | + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { |
| 21 | + str.replace(start_pos, from.length(), to); |
| 22 | + start_pos += to.length(); |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +std::string parse_binds(const std::string& filepath, const std::string& category) { |
| 27 | + std::ifstream file(filepath); |
| 28 | + if (!file.is_open()) return ""; // Silently ignore if file doesn't exist |
| 29 | + |
| 30 | + std::ostringstream output; |
| 31 | + output << " ────────────────── " << category << " ────────────────── \n"; |
| 32 | + |
| 33 | + std::string line; |
| 34 | + std::regex bind_regex(R"(^\s*bind[a-z]*\s*=\s*(.*))"); |
| 35 | + std::smatch match; |
| 36 | + |
| 37 | + while (std::getline(file, line)) { |
| 38 | + if (std::regex_match(line, match, bind_regex)) { |
| 39 | + std::string payload = match[1].str(); |
| 40 | + |
| 41 | + std::vector<std::string> parts; |
| 42 | + std::stringstream ss(payload); |
| 43 | + std::string item; |
| 44 | + while (std::getline(ss, item, ',')) { |
| 45 | + parts.push_back(item); |
| 46 | + } |
| 47 | + |
| 48 | + if (parts.size() >= 3) { |
| 49 | + std::string mod = trim(parts[0]); |
| 50 | + replace_all(mod, "$mainMod", "SUPER"); |
| 51 | + |
| 52 | + std::string key = trim(parts[1]); |
| 53 | + |
| 54 | + std::string action; |
| 55 | + for (size_t i = 2; i < parts.size(); ++i) { |
| 56 | + action += parts[i]; |
| 57 | + if (i != parts.size() - 1) action += ","; |
| 58 | + } |
| 59 | + action = trim(action); |
| 60 | + |
| 61 | + std::regex comment_regex(R"(\s*#\s*)"); |
| 62 | + action = std::regex_replace(action, comment_regex, " ▏ "); |
| 63 | + |
| 64 | + std::string keys_combo = "[" + mod + " + " + key + "]"; |
| 65 | + |
| 66 | + std::ostringstream line_out; |
| 67 | + line_out << std::left << std::setw(25) << keys_combo << " " << action << "\n"; |
| 68 | + output << line_out.str(); |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + return output.str(); |
| 73 | +} |
| 74 | + |
| 75 | +int main() { |
| 76 | + const char* home_env = std::getenv("HOME"); |
| 77 | + if (!home_env) { |
| 78 | + std::cerr << "Error: HOME environment variable not set.\n"; |
| 79 | + return 1; |
| 80 | + } |
| 81 | + std::string home(home_env); |
| 82 | + |
| 83 | + std::string core_conf = home + "/.config/hypr/conf.d/08-keybinds.conf"; |
| 84 | + std::string user_conf = home + "/.config/hypr/user/configs/keybinds.conf"; |
| 85 | + |
| 86 | + std::string full_output = parse_binds(core_conf, "SYSTEM CORE BINDS"); |
| 87 | + full_output += parse_binds(user_conf, "USER OVERRIDES"); |
| 88 | + |
| 89 | + if (full_output.empty() || full_output.find("SUPER") == std::string::npos) { |
| 90 | + std::cerr << "No keybinds found or config missing.\n"; |
| 91 | + return 1; |
| 92 | + } |
| 93 | + |
| 94 | + std::string rofi_cmd = R"(rofi -dmenu -i -p " Search Keybinds" -theme-str 'window { width: 900px; border-radius: 12px; } listview { lines: 20; fixed-height: true; } element-text { font: "monospace 11"; }')"; |
| 95 | + |
| 96 | + FILE* pipe = popen(rofi_cmd.c_str(), "w"); |
| 97 | + if (!pipe) { |
| 98 | + std::cerr << "Failed to open pipe to Rofi.\n"; |
| 99 | + return 1; |
| 100 | + } |
| 101 | + |
| 102 | + fwrite(full_output.c_str(), 1, full_output.size(), pipe); |
| 103 | + pclose(pipe); |
| 104 | + |
| 105 | + return 0; |
| 106 | +} |
0 commit comments