// gKeyMapper - mappings file parsing/formatting. #include "mapping.h" #include #include #include #include #include "keys.h" namespace gkeymapper { const char* action_type_token(ActionType type) { switch (type) { case ActionType::Key: return "key"; case ActionType::Command: return "command"; case ActionType::KeyHold: return "keyhold"; case ActionType::Disabled: default: return "disabled"; } } ActionType action_type_from_token(const std::string& token) { if (token == "key") { return ActionType::Key; } if (token == "command") { return ActionType::Command; } if (token == "keyhold") { return ActionType::KeyHold; } return ActionType::Disabled; } std::string format_mappings(const MappingSet& set) { std::string out = "# gKeyMapper mappings (G1..G9)\n"; for (int i = 0; i < kNumKeys; ++i) { Mapping mapping; if (i < static_cast(set.keys.size())) { mapping = set.keys[i]; } out += "G"; out += std::to_string(i + 1); out += "="; out += action_type_token(mapping.type); out += ":"; out += mapping.target; out += "\n"; } return out; } MappingSet parse_mappings(const std::string& text) { MappingSet set; set.keys.assign(kNumKeys, Mapping{}); std::istringstream in(text); std::string line; while (std::getline(in, line)) { if (line.empty() || line[0] == '#') { continue; } auto eq = line.find('='); if (eq == std::string::npos) { continue; } std::string key = line.substr(0, eq); if (key.size() < 2 || key[0] != 'G') { continue; } int n = 0; try { n = std::stoi(key.substr(1)) - 1; } catch (...) { continue; } if (n < 0 || n >= kNumKeys) { continue; } std::string rest = line.substr(eq + 1); auto col = rest.find(':'); std::string typeToken = (col == std::string::npos) ? rest : rest.substr(0, col); std::string target = (col == std::string::npos) ? std::string() : rest.substr(col + 1); set.keys[n].type = action_type_from_token(typeToken); set.keys[n].target = target; } return set; } bool load_mappings_file(const std::string& path, MappingSet& out) { std::ifstream in(path); if (!in) { return false; } std::stringstream buffer; buffer << in.rdbuf(); out = parse_mappings(buffer.str()); return true; } bool save_mappings_file(const std::string& path, const MappingSet& set) { // Write a temporary file first and rename it into place: a reader (the // daemon) either sees the old or the new file, never a half-written one. const std::string tempPath = path + ".tmp"; { std::ofstream out(tempPath); if (!out) { return false; } out << format_mappings(set); out.flush(); if (!out) { return false; } } return std::rename(tempPath.c_str(), path.c_str()) == 0; } std::string default_config_path() { const char* home = std::getenv("HOME"); return std::string(home ? home : "") + "/.config/gKeyMapper/mappings.conf"; } std::string format_shortcut_step(unsigned modifiers, const std::string& key_name) { std::string step; if ((modifiers & kModCtrl) != 0) { step += "ctrl+"; } if ((modifiers & kModAlt) != 0) { step += "alt+"; } if ((modifiers & kModShift) != 0) { step += "shift+"; } if ((modifiers & kModSuper) != 0) { step += "super+"; } if ((modifiers & kModAltGr) != 0) { step += "altgr+"; } step += key_name; return step; } bool parse_shortcut(const std::string& text, std::vector& out, const KeyResolver& resolve) { out.clear(); if (text.empty()) { return false; } std::istringstream combos(text); std::string combo; while (std::getline(combos, combo, ',')) { if (combo.empty()) { return false; } ShortcutStep step; bool haveKey = false; std::istringstream parts(combo); std::string part; while (std::getline(parts, part, '+')) { if (part.empty()) { return false; } unsigned mod = modifier_bit(part); if (mod != kModNone) { step.modifiers |= mod; continue; } int code = resolve ? resolve(part) : linux_key_code(part); if (code < 0) { return false; // unknown key name } step.key_name = part; step.linux_key = code; haveKey = true; } if (!haveKey) { return false; // combo without an actual key } out.push_back(step); } return !out.empty(); } } // namespace gkeymapper