--- a/src/config-merge.hpp +++ b/src/config-merge.hpp @@ -28,6 +28,16 @@ for (size_t pos = 0; pos < source.size();) { auto end = source.find('\n', pos); + // A trailing unescaped backslash joins physical lines. Retain the + // original bytes so editing another key preserves these commands. + while (end != source.npos) + { + auto tail = end; + if (tail > pos && source[tail - 1] == '\r') --tail; + if (tail <= pos || source[tail - 1] != '\\' || + (tail > pos + 1 && source[tail - 2] == '\\')) break; + end = source.find('\n', end + 1); + } const size_t count = end == source.npos ? source.size() - pos : end - pos + 1; const size_t index = lines.size(); lines.push_back(source.substr(pos, count)); pos += count; const auto& raw = lines.back(); @@ -47,7 +57,7 @@ const auto key = section + "/" + raw.substr(first, name_end - first + 1); size_t last = raw.size(); for (size_t i = equal + 1; i < raw.size(); ++i) - if ((raw[i] == '#' || raw[i] == ';') && raw[i - 1] != '\\') { last = i; break; } + if (raw[i] == '#' && raw[i - 1] != '\\') { last = i; break; } const auto start = raw.find_first_not_of(" \t", equal + 1); const auto finish = raw.find_last_not_of(" \t\r\n", last ? last - 1 : 0); const size_t a = start == raw.npos || start >= last ? equal + 1 : start; @@ -91,6 +101,16 @@ auto previous = original.entries.find(key); if (previous != original.entries.end() && previous->second.value == entry.value) continue; Ini current(result); current.set(key, entry.value); result = current.bytes(); + } + // Compound options (commands, bindings, rules) can remove individual keys. + // Only remove keys that this editor actually had in its previous snapshot. + for (const auto& [key, entry] : original.entries) + { + if (edited.entries.count(key)) continue; + Ini current(result); + if (auto found = current.entries.find(key); found != current.entries.end()) + current.lines.erase(current.lines.begin() + found->second.line); + result = current.bytes(); } return result; } --- a/src/displays.cpp +++ b/src/displays.cpp @@ -1,3 +1,4 @@ +#include #include "displays.hpp" #include "backgrounds.hpp" #include "ipc.hpp" @@ -120,7 +121,7 @@ position.set_placeholder_text("auto or x,y"); field(monitor_grid, 2, "Desktop position", position); scale.set_range(.25, 8); scale.set_increments(.25, 1); scale.set_digits(2); scale.set_value(1.0); scale.get_accessible()->set_name("Physical monitor scale"); field(monitor_grid, 3, "Scale", scale); - for (auto text : {"normal", "90", "180", "270", "flipped", "flipped-90", "flipped-180", "flipped-270"}) rotation.append(text, text); + for (auto text : wf::output_transform_names) rotation.append(std::string(text), std::string(text)); field(monitor_grid, 4, "Rotation", rotation); local_gap.set_range(-1, 1000); local_gap.set_increments(1, 5); local_gap.get_accessible()->set_name("Physical monitor zone gap"); @@ -303,7 +304,8 @@ if (added.insert(current).second) mode.append(current, mode_label(current)); mode.set_active_id(current); position.set_text(get(pre + "position")); scale.set_value(std::stod(get(pre + "scale"))); - rotation.set_active_id(get(pre + "transform")); + auto transform = wf::parse_output_transform(get(pre + "transform")); + rotation.set_active_id(transform ? std::string(wf::output_transform_names.at(*transform)) : ""); local_gap.set_value(std::stoi(get(pre + "zone_gap"))); include.set_active(std::find(order.begin(), order.end(), selected) != order.end()); loading = false; update_controls(); diagram.queue_draw();