Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
properties.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "properties.hh"
3#include "jsonipc/jsonipc.hh"
4#include "strings.hh"
5#include "utils.hh"
6#include "main.hh"
7#include "path.hh"
8#include "serialize.hh"
9#include "internal.hh"
10
11#define PDEBUG(...) Ase::debug ("prefs", __VA_ARGS__)
12
13namespace Ase {
14
15// == Property ==
16Property::Property() :
17 name (this, "name"),
18 value (this, "value"),
19 metadata (this, "metadata")
20{}
21
22Property::~Property()
23{}
24
26Property::get_name () const
27{
28 return ident();
29}
30
31void
32Property::set_name (const String &n)
33{
34 // not implemented
35}
36
37void
38Property::set_metadata (const StringS &md)
39{
40 // not implemented
41}
42
43// == PropertyImpl ==
44PropertyImpl::PropertyImpl (const Param &param, const PropertyGetter &getter,
45 const PropertySetter &setter, const PropertyLister &lister) :
46 getter_ (getter), setter_ (setter), lister_ (lister)
47{
48 parameter_ = std::make_shared<Parameter> (param);
49}
50
51// == Preference ==
52using PrefsValueCallbackList = CallbackList<CString,const Value&>;
53struct PrefsValue {
54 ParameterC parameter;
55 Value value;
57};
59
61static CStringS notify_preference_queue;
62
63static PrefsMap&
64prefs_map()
65{
66 static PrefsMap *const prefsmap = []() { return new PrefsMap(); } ();
67 return *prefsmap;
68}
69
70static bool preferences_autosave = false;
71static uint timerid_maybe_save_preferences = 0;
72
73static void
74maybe_save_preferences()
75{
76 main_loop->clear_source (&timerid_maybe_save_preferences);
77 if (preferences_autosave && notify_preference_queue.empty())
78 Preference::save_preferences();
79}
80
81static void
82notify_preference_listeners ()
83{
84 CStringS changed_prefs (notify_preference_queue.begin(), notify_preference_queue.end()); // converts CStringS to StringS
85 notify_preference_queue.clear(); // clear, queue taken over
86 return_unless (!changed_prefs.empty());
87 std::sort (changed_prefs.begin(), changed_prefs.end()); // sort and dedup
88 changed_prefs.erase (std::unique (changed_prefs.begin(), changed_prefs.end()), changed_prefs.end());
89 // emit "notify" on individual preferences
90 PrefsMap &prefsmap = prefs_map();
91 for (auto cident : changed_prefs) {
92 PrefsValue &pv = prefsmap[cident];
93 (*pv.callbacks) (cident, pv.value);
94 }
95 // notify preference list listeners
96 const auto callbacklist = prefs_callbacks; // keep reference around invocation
97 (*callbacklist) (changed_prefs);
98 if (preferences_autosave)
99 main_loop->exec_once (577, &timerid_maybe_save_preferences, maybe_save_preferences);
100 else
101 main_loop->clear_source (&timerid_maybe_save_preferences);
102}
103
104static void
105queue_notify_preference_listeners (const CString &cident)
106{
107 return_unless (main_loop != nullptr);
108 // enqueue idle handler for accumulated preference change notifications
109 const bool need_enqueue = notify_preference_queue.empty();
110 notify_preference_queue.push_back (cident);
111 if (need_enqueue)
112 main_loop->exec_now (notify_preference_listeners);
113}
114
115Preference::Preference (ParameterC parameter)
116{
117 parameter_ = parameter;
118 PrefsMap &prefsmap = prefs_map(); // Preference must already be registered
119 auto it = prefsmap.find (parameter_->cident);
120 assert_return (it != prefsmap.end());
121 PrefsValue &pv = it->second;
122 sigh_ = pv.callbacks->add_delcb ([this] (const String &ident, const Value &value) { emit_event ("notify", ident); });
123}
124
125Preference::Preference (const Param &param, const StringValueF &cb)
126{
127 assert_return (param.ident.empty() == false);
128 parameter_ = std::make_shared<Parameter> (param);
129 PrefsMap &prefsmap = prefs_map();
130 PrefsValue &pv = prefsmap[parameter_->cident];
131 assert_return (pv.parameter == nullptr); // catch duplicate registration
132 pv.parameter = parameter_;
133 pv.value = pv.parameter->initial();
134 pv.callbacks = PrefsValueCallbackList::make_shared();
135 sigh_ = pv.callbacks->add_delcb ([this] (const String &ident, const Value &value) { emit_event ("notify", ident); });
136 queue_notify_preference_listeners (parameter_->cident);
137 if (cb) {
138 Connection connection = on_event ("notify", [this,cb] (const Event &event) { cb (this->parameter_->cident, this->get_value()); });
139 connection_ = new Connection (connection);
140 }
141}
142
143Preference::~Preference()
144{
145 if (connection_) {
146 connection_->disconnect();
147 delete connection_;
148 connection_ = nullptr;
149 }
150 if (sigh_)
151 sigh_(); // delete signal handler callback
152}
153
154Value
155Preference::get_value () const
156{
157 PrefsMap &prefsmap = prefs_map();
158 PrefsValue &pv = prefsmap[parameter_->cident];
159 return pv.value;
160}
161
162bool
163Preference::set_value (const Value &v)
164{
165 PrefsMap &prefsmap = prefs_map();
166 PrefsValue &pv = prefsmap[parameter_->cident];
167 Value next = parameter_->constrain (v);
168 const bool changed = next == pv.value;
169 pv.value = std::move (next);
170 queue_notify_preference_listeners (parameter_->cident); // delayed
171 this->value.notify();
172 return changed;
173}
174
175Value
176Preference::get (const String &ident)
177{
178 const CString cident = CString::lookup (ident);
179 return_unless (!cident.empty(), {});
180 PrefsMap &prefsmap = prefs_map();
181 auto it = prefsmap.find (cident);
182 return_unless (it != prefsmap.end(), {});
183 PrefsValue &pv = it->second;
184 return pv.value;
185}
186
187PreferenceP
188Preference::find (const String &ident)
189{
190 const CString cident = CString::lookup (ident);
191 return_unless (!cident.empty(), {});
192 PrefsMap &prefsmap = prefs_map();
193 auto it = prefsmap.find (cident);
194 return_unless (it != prefsmap.end(), {});
195 PrefsValue &pv = it->second;
196 return Preference::make_shared (pv.parameter);
197}
198
199CStringS
200Preference::list ()
201{
202 CStringS strings;
203 for (const auto &e : prefs_map())
204 strings.push_back (e.first);
205 std::sort (strings.begin(), strings.end());
206 return strings;
207}
208
209Preference::DelCb
210Preference::listen (const std::function<void(const CStringS&)> &func)
211{
212 return prefs_callbacks->add_delcb (func);
213}
214
215static String
216pathname_anklangrc()
217{
218 static const String anklangrc = Path::join (Path::config_home(), "anklang", "anklangrc.json");
219 return anklangrc;
220}
221
222void
223Preference::load_preferences (bool autosave)
224{
225 const String jsontext = Path::stringread (pathname_anklangrc());
226 ValueR precord;
227 json_parse (jsontext, precord);
228 for (ValueField vf : precord) {
229 PreferenceP pref = find (vf.name);
230 PDEBUG ("%s: %s %s=%s\n", __func__, pref ? "loading" : "ignoring", vf.name, vf.value->repr());
231 if (pref)
232 pref->set_value (*vf.value);
233 }
234 preferences_autosave = autosave;
235}
236
237void
238Preference::save_preferences ()
239{
240 ValueR precord;
241 for (auto ident : list())
242 precord[ident] = get (ident);
243 const String new_jsontext = json_stringify (precord, Writ::RELAXED | Writ::SKIP_EMPTYSTRING) + "\n";
244 const String cur_jsontext = Path::stringread (pathname_anklangrc());
245 if (new_jsontext != cur_jsontext) {
246 PDEBUG ("%s: %s\n", __func__, precord.repr());
247 Path::stringwrite (pathname_anklangrc(), new_jsontext, true);
248 }
249}
250
251} // Ase
T begin(T... args)
Compact, deduplicating string variant for constant strings.
Definition memory.hh:138
Reentrant callback list with configurable arguments.
Definition callback.hh:13
T clear(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
The Anklang C++ API namespace.
Definition api.hh:9
bool json_parse(const String &jsonstring, T &target)
Parse a well formed JSON string and assign contents to target.
Definition serialize.hh:538
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
Definition serialize.hh:530
T push_back(T... args)
T sort(T... args)
Value type used to interface with various property types.
Definition value.hh:54
T unique(T... args)