diff --git a/src/solanaceae/crdtnotes/crdtnotes.cpp b/src/solanaceae/crdtnotes/crdtnotes.cpp index ab6fcd5..0043367 100644 --- a/src/solanaceae/crdtnotes/crdtnotes.cpp +++ b/src/solanaceae/crdtnotes/crdtnotes.cpp @@ -37,3 +37,20 @@ CRDTNotes::Doc* CRDTNotes::addDoc(const CRDTAgent& self_agent, const DocID& id) return &doc; } +void CRDTNotes::writeLockRelease(const DocID& id) { + assert(_doc_write_locks.count(id) > 0); + _doc_write_locks.erase(id); +} + +bool CRDTNotes::isWriteLocked(const DocID& id) const { + return _doc_write_locks.count(id); +} + +std::optional CRDTNotes::writeLockAquire(const DocID& id) { + if (_doc_write_locks.count(id)) { + return std::nullopt; // replace with exception instead? + } + + _doc_write_locks.emplace(id); + return DocWriteLock{*this, id}; +} diff --git a/src/solanaceae/crdtnotes/crdtnotes.hpp b/src/solanaceae/crdtnotes/crdtnotes.hpp index 20ee2d4..26b0bb9 100644 --- a/src/solanaceae/crdtnotes/crdtnotes.hpp +++ b/src/solanaceae/crdtnotes/crdtnotes.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include using ID32 = std::array; @@ -37,9 +39,23 @@ class CRDTNotes { uint64_t seq{0}; }; + // RAII lock wrapper + struct DocWriteLock { + CRDTNotes* notes; + DocID id; + + // ctr assumes lock + DocWriteLock(CRDTNotes& notes, const DocID& id) : notes(¬es), id(id) {} + DocWriteLock(const DocWriteLock&) = delete; + DocWriteLock(DocWriteLock&& other) : notes(other.notes), id(other.id) { other.notes = nullptr; } + ~DocWriteLock(void) { if (notes) { notes->writeLockRelease(id); } } + bool operator==(const DocWriteLock& other) const { return id == other.id; } + }; + private: // TODO: add metadata to docs std::unordered_map _docs; + std::unordered_set _doc_write_locks; public: // config? @@ -52,5 +68,16 @@ class CRDTNotes { Doc* getDoc(const DocID& id); Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc); + + void writeLockRelease(const DocID& id); + bool isWriteLocked(const DocID& id) const; + std::optional writeLockAquire(const DocID& id); +}; + +template<> +struct std::hash { + std::uint64_t operator()(const CRDTNotes::DocWriteLock& s) const noexcept { + return std::hash{}(s.id); + } }; diff --git a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp index aea12c6..cd2d1fc 100644 --- a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp +++ b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp @@ -48,6 +48,12 @@ namespace detail { } // detail +std::unordered_set::iterator CRDTNotesImGui::findLock(const CRDTNotes::DocID& doc_id) { + auto it = _held_locks.begin(); + for (; it != _held_locks.end() && it->id != doc_id; it++) {} + return it; +} + CRDTNotesImGui::CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs) : _notes(notes), _notes_sync(notes_sync), _cs(cs) { } @@ -142,17 +148,36 @@ bool CRDTNotesImGui::renderDoc(const CRDTNotes::DocID& doc_id) { return false; } + auto lock_it = findLock(doc_id); + bool self_held = lock_it != _held_locks.end(); + const bool foreign_held = !self_held && _notes.isWriteLocked(doc_id); + auto text = doc->getText(); - if (renderDocText(text)) { + ImGui::InputTextMultiline( + "##doc", + &text, + {-1,-1}, + ImGuiInputTextFlags_AllowTabInput | + (foreign_held ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None) | + ImGuiInputTextFlags_CallbackAlways + //cb, + //&text + ); + if (!foreign_held && !self_held && (ImGui::IsItemActive() || ImGui::IsItemEdited())) { + // TODO: check + _held_locks.emplace(_notes.writeLockAquire(doc_id).value()); + self_held = true; + std::cout << "!!!! imgui lock aquired\n"; + } else if (!foreign_held && self_held && !(ImGui::IsItemActive() || ImGui::IsItemEdited())) { + // release lock + _held_locks.erase(lock_it); + std::cout << "!!!! imgui lock released\n"; + } + + if (self_held && ImGui::IsItemEdited()) { _notes_sync.merge(doc_id, text); return true; } return false; } - -bool CRDTNotesImGui::renderDocText(std::string& text) const { - // TODO: replace with text editor (zep) or visualize stuff?? - return ImGui::InputTextMultiline("##doc", &text, {-1,-1}, ImGuiInputTextFlags_AllowTabInput); -} - diff --git a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp index b0fb52a..017ef89 100644 --- a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp +++ b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp @@ -4,6 +4,7 @@ #include #include +#include class CRDTNotesImGui { CRDTNotes& _notes; @@ -13,6 +14,9 @@ class CRDTNotesImGui { bool _show_global_list {true}; std::set _open_docs; + std::unordered_set _held_locks; + + std::unordered_set::iterator findLock(const CRDTNotes::DocID& doc_id); public: CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs); @@ -22,6 +26,5 @@ class CRDTNotesImGui { bool renderContactListContactSmall(const Contact4 c, const bool selected) const; bool renderDoc(const CRDTNotes::DocID& doc_id); - bool renderDocText(std::string& text) const; };