Listener object is better than 3 lambdas + handle
This commit is contained in:
parent
d2334af286
commit
30d7196cd4
4 changed files with 188 additions and 121 deletions
|
@ -10,9 +10,34 @@
|
|||
|
||||
namespace command
|
||||
{
|
||||
class set;
|
||||
class base;
|
||||
class factory_base;
|
||||
|
||||
/**
|
||||
* Set add/drop listener.
|
||||
*/
|
||||
class set_listener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Dtor.
|
||||
*/
|
||||
virtual ~set_listener();
|
||||
/**
|
||||
* New item in set.
|
||||
*/
|
||||
virtual void create(set& s, const std::string& name, factory_base& cmd) = 0;
|
||||
/**
|
||||
* Deleted item from set.
|
||||
*/
|
||||
virtual void destroy(set& s, const std::string& name) = 0;
|
||||
/**
|
||||
* Destroyed the entiere set.
|
||||
*/
|
||||
virtual void kill(set& s) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of commands.
|
||||
*/
|
||||
|
@ -38,20 +63,15 @@ public:
|
|||
/**
|
||||
* Add a notification callback and call ccb on all.
|
||||
*
|
||||
* Parameter ccb: The create callback function.
|
||||
* Parameter dcb: The destroy callback function.
|
||||
* Parameter tcb: The terminate callback function.
|
||||
* Returns: Callback handle.
|
||||
* Parameter listener: The listener to add.
|
||||
*/
|
||||
uint64_t add_callback(std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb,
|
||||
std::function<void(set& s, const std::string& name)> dcb, std::function<void(set& s)> tcb)
|
||||
throw(std::bad_alloc);
|
||||
void add_callback(set_listener& listener) throw(std::bad_alloc);
|
||||
/**
|
||||
* Drop a notification callback and call dcb on all.
|
||||
*
|
||||
* Parameter handle: The handle of callback to drop.
|
||||
* Parameter listener: The listener to drop.
|
||||
*/
|
||||
void drop_callback(uint64_t handle) throw();
|
||||
void drop_callback(set_listener& listener) throw();
|
||||
/**
|
||||
* Obtain list of all commands so far.
|
||||
*/
|
||||
|
@ -121,6 +141,17 @@ public:
|
|||
*/
|
||||
void set_oom_panic(void (*fn)());
|
||||
private:
|
||||
class listener : public set_listener
|
||||
{
|
||||
public:
|
||||
listener(group& _grp);
|
||||
~listener();
|
||||
void create(set& s, const std::string& name, factory_base& cmd);
|
||||
void destroy(set& s, const std::string& name);
|
||||
void kill(set& s);
|
||||
private:
|
||||
group& grp;
|
||||
} _listener;
|
||||
std::set<std::string> command_stack;
|
||||
std::map<std::string, std::list<std::string>> aliases;
|
||||
std::ostream* output;
|
||||
|
|
|
@ -74,6 +74,32 @@ struct keyspec
|
|||
};
|
||||
|
||||
class invbind_set;
|
||||
class invbind_info;
|
||||
|
||||
/**
|
||||
* Set add/drop listener.
|
||||
*/
|
||||
class set_listener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Dtor.
|
||||
*/
|
||||
virtual ~set_listener();
|
||||
/**
|
||||
* New item in set.
|
||||
*/
|
||||
virtual void create(invbind_set& s, const std::string& name, invbind_info& ibinfo) = 0;
|
||||
/**
|
||||
* Deleted item from set.
|
||||
*/
|
||||
virtual void destroy(invbind_set& s, const std::string& name) = 0;
|
||||
/**
|
||||
* Destroyed the entiere set.
|
||||
*/
|
||||
virtual void kill(invbind_set& s) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Keyboard mapper. Maps keyboard keys into commands.
|
||||
|
@ -215,6 +241,17 @@ private:
|
|||
key* _key;
|
||||
unsigned subkey;
|
||||
};
|
||||
class listener : public set_listener
|
||||
{
|
||||
public:
|
||||
listener(mapper& _grp);
|
||||
~listener();
|
||||
void create(invbind_set& s, const std::string& name, invbind_info& ibinfo);
|
||||
void destroy(invbind_set& s, const std::string& name);
|
||||
void kill(invbind_set& s);
|
||||
private:
|
||||
mapper& grp;
|
||||
} _listener;
|
||||
void change_command(const keyspec& spec, const std::string& old, const std::string& newc);
|
||||
void on_key_event(modifier_set& mods, key& key, event& event);
|
||||
void on_key_event_subkey(modifier_set& mods, key& key, unsigned skey, bool polarity);
|
||||
|
@ -224,7 +261,7 @@ private:
|
|||
std::map<std::string, ctrlrkey*> ckeys;
|
||||
std::map<triplet, std::string> bindings;
|
||||
std::set<key*> listening;
|
||||
std::map<invbind_set*, uint64_t> invbind_set_cbs;
|
||||
std::set<invbind_set*> invbind_set_cbs;
|
||||
keyboard& kbd;
|
||||
command::group& domain;
|
||||
bool dtor_running;
|
||||
|
@ -257,13 +294,11 @@ public:
|
|||
/**
|
||||
* Add a callback on new invese bind.
|
||||
*/
|
||||
uint64_t add_callback(std::function<void(invbind_set& s, const std::string& name, invbind_info& ib)> ccb,
|
||||
std::function<void(invbind_set& s, const std::string& name)> dcb,
|
||||
std::function<void(invbind_set& s)> tcb) throw(std::bad_alloc);
|
||||
void add_callback(set_listener& listener) throw(std::bad_alloc);
|
||||
/**
|
||||
* Drop a callback on new inverse bind.
|
||||
*/
|
||||
void drop_callback(uint64_t handle);
|
||||
void drop_callback(set_listener& listener);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -65,13 +65,6 @@ namespace
|
|||
exit(1);
|
||||
}
|
||||
|
||||
struct set_callbacks
|
||||
{
|
||||
std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb;
|
||||
std::function<void(set& s, const std::string& name)> dcb;
|
||||
std::function<void(set& s)> tcb;
|
||||
};
|
||||
|
||||
threads::rlock* global_lock;
|
||||
threads::rlock& get_cmd_lock()
|
||||
{
|
||||
|
@ -81,21 +74,24 @@ namespace
|
|||
|
||||
struct set_internal
|
||||
{
|
||||
std::map<uint64_t, set_callbacks> callbacks;
|
||||
integer_pool pool;
|
||||
std::set<set_listener*> callbacks;
|
||||
std::map<std::string, factory_base*> commands;
|
||||
};
|
||||
|
||||
struct group_internal
|
||||
{
|
||||
std::map<std::string, base*> commands;
|
||||
std::map<set*, uint64_t> set_handles;
|
||||
std::set<set*> set_handles;
|
||||
};
|
||||
|
||||
typedef stateobject::type<set, set_internal> set_internal_t;
|
||||
typedef stateobject::type<group, group_internal> group_internal_t;
|
||||
}
|
||||
|
||||
set_listener::~set_listener()
|
||||
{
|
||||
}
|
||||
|
||||
void factory_base::_factory_base(set& _set, const std::string& cmd) throw(std::bad_alloc)
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
|
@ -161,10 +157,10 @@ set::~set() throw()
|
|||
//Call all DCBs on all factories.
|
||||
for(auto i : state->commands)
|
||||
for(auto j : state->callbacks)
|
||||
j.second.dcb(*this, i.first);
|
||||
j->destroy(*this, i.first);
|
||||
//Call all TCBs.
|
||||
for(auto j : state->callbacks)
|
||||
j.second.tcb(*this);
|
||||
j->kill(*this);
|
||||
//Notify all factories that base set died.
|
||||
for(auto i : state->commands)
|
||||
i.second->set_died();
|
||||
|
@ -183,7 +179,7 @@ void set::do_register(const std::string& name, factory_base& cmd) throw(std::bad
|
|||
state.commands[name] = &cmd;
|
||||
//Call all CCBs on this.
|
||||
for(auto i : state.callbacks)
|
||||
i.second.ccb(*this, name, cmd);
|
||||
i->create(*this, name, cmd);
|
||||
}
|
||||
|
||||
void set::do_unregister(const std::string& name, factory_base& cmd) throw(std::bad_alloc)
|
||||
|
@ -195,43 +191,30 @@ void set::do_unregister(const std::string& name, factory_base& cmd) throw(std::b
|
|||
state->commands.erase(name);
|
||||
//Call all DCBs on this.
|
||||
for(auto i : state->callbacks)
|
||||
i.second.dcb(*this, name);
|
||||
i->destroy(*this, name);
|
||||
}
|
||||
|
||||
uint64_t set::add_callback(std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb,
|
||||
std::function<void(set& s, const std::string& name)> dcb, std::function<void(set& s)> tcb)
|
||||
void set::add_callback(set_listener& listener)
|
||||
throw(std::bad_alloc)
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
auto& state = set_internal_t::get(this);
|
||||
set_callbacks cb;
|
||||
cb.ccb = ccb;
|
||||
cb.dcb = dcb;
|
||||
cb.tcb = tcb;
|
||||
uint64_t i = state.pool();
|
||||
try {
|
||||
state.callbacks[i] = cb;
|
||||
} catch(...) {
|
||||
state.pool(i);
|
||||
throw;
|
||||
}
|
||||
state.callbacks.insert(&listener);
|
||||
//To avoid races, call CCBs on all factories for this.
|
||||
for(auto j : state.commands)
|
||||
ccb(*this, j.first, *j.second);
|
||||
return i;
|
||||
listener.create(*this, j.first, *j.second);
|
||||
}
|
||||
|
||||
void set::drop_callback(uint64_t handle) throw()
|
||||
void set::drop_callback(set_listener& listener) throw()
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
auto state = set_internal_t::get_soft(this);
|
||||
if(!state) return;
|
||||
if(state->callbacks.count(handle)) {
|
||||
if(state->callbacks.count(&listener)) {
|
||||
//To avoid races, call DCBs on all factories for this.
|
||||
for(auto j : state->commands)
|
||||
state->callbacks[handle].dcb(*this, j.first);
|
||||
state->callbacks.erase(handle);
|
||||
state->pool(handle);
|
||||
listener.destroy(*this, j.first);
|
||||
state->callbacks.erase(&listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,6 +227,7 @@ std::map<std::string, factory_base*> set::get_commands()
|
|||
}
|
||||
|
||||
group::group() throw(std::bad_alloc)
|
||||
: _listener(*this)
|
||||
{
|
||||
oom_panic_routine = default_oom_panic;
|
||||
output = &std::cerr;
|
||||
|
@ -264,7 +248,7 @@ group::~group() throw()
|
|||
|
||||
//Drop all callbacks.
|
||||
for(auto i : state->set_handles)
|
||||
i.first->drop_callback(i.second);
|
||||
i->drop_callback(_listener);
|
||||
//We assume all bases that need destroying have already been destroyed.
|
||||
group_internal_t::clear(this);
|
||||
}
|
||||
|
@ -441,23 +425,10 @@ void group::add_set(set& s) throw(std::bad_alloc)
|
|||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
auto& state = group_internal_t::get(this);
|
||||
if(state.set_handles.count(&s))
|
||||
return;
|
||||
state.set_handles[&s] = 0xFFFFFFFFFFFFFFFF;
|
||||
if(state.set_handles.count(&s)) return;
|
||||
try {
|
||||
state.set_handles[&s] = s.add_callback(
|
||||
[this](set& s, const std::string& name, factory_base& cmd) {
|
||||
cmd.make(*this);
|
||||
}, [this](set& s, const std::string& name) {
|
||||
auto state = group_internal_t::get_soft(this);
|
||||
if(!state) return;
|
||||
state->commands.erase(name);
|
||||
}, [this](set& s) {
|
||||
auto state = group_internal_t::get_soft(this);
|
||||
if(!state) return;
|
||||
state->set_handles.erase(&s);
|
||||
}
|
||||
);
|
||||
state.set_handles.insert(&s);
|
||||
s.add_callback(_listener);
|
||||
} catch(...) {
|
||||
state.set_handles.erase(&s);
|
||||
}
|
||||
|
@ -468,10 +439,39 @@ void group::drop_set(set& s) throw()
|
|||
threads::arlock h(get_cmd_lock());
|
||||
auto state = group_internal_t::get_soft(this);
|
||||
if(!state) return;
|
||||
if(!state->set_handles.count(&s))
|
||||
return;
|
||||
//Drop the callback. This unregisters all.
|
||||
s.drop_callback(state->set_handles[&s]);
|
||||
s.drop_callback(_listener);
|
||||
state->set_handles.erase(&s);
|
||||
}
|
||||
|
||||
group::listener::listener(group& _grp)
|
||||
: grp(_grp)
|
||||
{
|
||||
}
|
||||
|
||||
group::listener::~listener()
|
||||
{
|
||||
}
|
||||
|
||||
void group::listener::create(set& s, const std::string& name, factory_base& cmd)
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
cmd.make(grp);
|
||||
}
|
||||
|
||||
void group::listener::destroy(set& s, const std::string& name)
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
auto state = group_internal_t::get_soft(&grp);
|
||||
if(!state) return;
|
||||
state->commands.erase(name);
|
||||
}
|
||||
|
||||
void group::listener::kill(set& s)
|
||||
{
|
||||
threads::arlock h(get_cmd_lock());
|
||||
auto state = group_internal_t::get_soft(&grp);
|
||||
if(!state) return;
|
||||
state->set_handles.erase(&s);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,22 +16,19 @@ namespace
|
|||
return *global_lock;
|
||||
}
|
||||
|
||||
struct set_callbacks
|
||||
{
|
||||
std::function<void(invbind_set& s, const std::string& name, invbind_info& cmd)> ccb;
|
||||
std::function<void(invbind_set& s, const std::string& name)> dcb;
|
||||
std::function<void(invbind_set& s)> tcb;
|
||||
};
|
||||
|
||||
struct set_internal
|
||||
{
|
||||
std::map<std::string, invbind_info*> invbinds;
|
||||
std::map<uint64_t, set_callbacks> callbacks;
|
||||
integer_pool pool;
|
||||
std::set<set_listener*> callbacks;
|
||||
};
|
||||
|
||||
typedef stateobject::type<invbind_set, set_internal> set_internal_t;
|
||||
}
|
||||
|
||||
set_listener::~set_listener()
|
||||
{
|
||||
}
|
||||
|
||||
std::string mapper::fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
|
||||
{
|
||||
if(cmd == "" || cmd == "*")
|
||||
|
@ -166,7 +163,7 @@ keyboard& mapper::get_keyboard() throw()
|
|||
}
|
||||
|
||||
mapper::mapper(keyboard& _kbd, command::group& _domain) throw(std::bad_alloc)
|
||||
: kbd(_kbd), domain(_domain)
|
||||
: _listener(*this), kbd(_kbd), domain(_domain)
|
||||
{
|
||||
register_queue<mapper, invbind>::do_ready(*this, true);
|
||||
register_queue<mapper, ctrlrkey>::do_ready(*this, true);
|
||||
|
@ -178,6 +175,8 @@ mapper::~mapper() throw()
|
|||
threads::arlock u(get_keymap_lock());
|
||||
for(auto i : ibinds)
|
||||
i.second->mapper_died();
|
||||
for(auto i : invbind_set_cbs)
|
||||
i->drop_callback(_listener);
|
||||
register_queue<mapper, invbind>::do_ready(*this, false);
|
||||
register_queue<mapper, ctrlrkey>::do_ready(*this, false);
|
||||
}
|
||||
|
@ -412,21 +411,10 @@ std::list<ctrlrkey*> mapper::get_controllerkeys_kbdkey(key* kbdkey)
|
|||
void mapper::add_invbind_set(invbind_set& set)
|
||||
{
|
||||
threads::arlock u(get_keymap_lock());
|
||||
if(invbind_set_cbs.count(&set))
|
||||
return;
|
||||
invbind_set_cbs[&set] = 0xFFFFFFFFFFFFFFFF;
|
||||
if(invbind_set_cbs.count(&set)) return;
|
||||
try {
|
||||
invbind_set_cbs[&set] = set.add_callback(
|
||||
[this](invbind_set& s, const std::string& name, invbind_info& ib) {
|
||||
ib.make(*this);
|
||||
}, [this](invbind_set& s, const std::string& name) {
|
||||
if(dtor_running) return;
|
||||
ibinds.erase(name);
|
||||
}, [this](invbind_set& s) {
|
||||
if(dtor_running) return;
|
||||
this->invbind_set_cbs.erase(&s);
|
||||
}
|
||||
);
|
||||
invbind_set_cbs.insert(&set);
|
||||
set.add_callback(_listener);
|
||||
} catch(...) {
|
||||
invbind_set_cbs.erase(&set);
|
||||
}
|
||||
|
@ -434,14 +422,41 @@ void mapper::add_invbind_set(invbind_set& set)
|
|||
|
||||
void mapper::drop_invbind_set(invbind_set& set)
|
||||
{
|
||||
threads::arlock u(get_keymap_lock());
|
||||
if(!invbind_set_cbs.count(&set))
|
||||
return;
|
||||
threads::arlock h(get_keymap_lock());
|
||||
//Drop the callback. This unregisters all.
|
||||
set.drop_callback(invbind_set_cbs[&set]);
|
||||
set.drop_callback(_listener);
|
||||
invbind_set_cbs.erase(&set);
|
||||
}
|
||||
|
||||
mapper::listener::listener(mapper& _grp)
|
||||
: grp(_grp)
|
||||
{
|
||||
}
|
||||
|
||||
mapper::listener::~listener()
|
||||
{
|
||||
}
|
||||
|
||||
void mapper::listener::create(invbind_set& s, const std::string& name, invbind_info& ibinfo)
|
||||
{
|
||||
threads::arlock h(get_keymap_lock());
|
||||
ibinfo.make(grp);
|
||||
}
|
||||
|
||||
void mapper::listener::destroy(invbind_set& s, const std::string& name)
|
||||
{
|
||||
threads::arlock h(get_keymap_lock());
|
||||
if(grp.dtor_running) return;
|
||||
grp.ibinds.erase(name);
|
||||
}
|
||||
|
||||
void mapper::listener::kill(invbind_set& s)
|
||||
{
|
||||
threads::arlock h(get_keymap_lock());
|
||||
if(grp.dtor_running) return;
|
||||
grp.invbind_set_cbs.erase(&s);
|
||||
}
|
||||
|
||||
invbind::invbind(mapper& kmapper, const std::string& _command, const std::string& _name, bool dynamic)
|
||||
throw(std::bad_alloc)
|
||||
: _mapper(&kmapper), cmd(_command), oname(_name)
|
||||
|
@ -533,10 +548,10 @@ invbind_set::~invbind_set()
|
|||
//Call all DCBs on all factories.
|
||||
for(auto i : state->invbinds)
|
||||
for(auto j : state->callbacks)
|
||||
j.second.dcb(*this, i.first);
|
||||
j->destroy(*this, i.first);
|
||||
//Call all TCBs.
|
||||
for(auto j : state->callbacks)
|
||||
j.second.tcb(*this);
|
||||
j->kill(*this);
|
||||
//Notify all factories that base set died.
|
||||
for(auto i : state->invbinds)
|
||||
i.second->set_died();
|
||||
|
@ -555,7 +570,7 @@ void invbind_set::do_register(const std::string& name, invbind_info& info)
|
|||
state.invbinds[name] = &info;
|
||||
//Call all CCBs on this.
|
||||
for(auto i : state.callbacks)
|
||||
i.second.ccb(*this, name, info);
|
||||
i->create(*this, name, info);
|
||||
}
|
||||
|
||||
void invbind_set::do_unregister(const std::string& name, invbind_info& info)
|
||||
|
@ -567,43 +582,29 @@ void invbind_set::do_unregister(const std::string& name, invbind_info& info)
|
|||
state->invbinds.erase(name);
|
||||
//Call all DCBs on this.
|
||||
for(auto i : state->callbacks)
|
||||
i.second.dcb(*this, name);
|
||||
i->destroy(*this, name);
|
||||
}
|
||||
|
||||
uint64_t invbind_set::add_callback(std::function<void(invbind_set& s, const std::string& name, invbind_info& ib)> ccb,
|
||||
std::function<void(invbind_set& s, const std::string& name)> dcb,
|
||||
std::function<void(invbind_set& s)> tcb) throw(std::bad_alloc)
|
||||
void invbind_set::add_callback(set_listener& listener) throw(std::bad_alloc)
|
||||
{
|
||||
threads::arlock u(get_keymap_lock());
|
||||
auto& state = set_internal_t::get(this);
|
||||
set_callbacks cb;
|
||||
cb.ccb = ccb;
|
||||
cb.dcb = dcb;
|
||||
cb.tcb = tcb;
|
||||
uint64_t i = state.pool();
|
||||
try {
|
||||
state.callbacks[i] = cb;
|
||||
} catch(...) {
|
||||
state.pool(i);
|
||||
throw;
|
||||
}
|
||||
state.callbacks.insert(&listener);
|
||||
//To avoid races, call CCBs on all factories for this.
|
||||
for(auto j : state.invbinds)
|
||||
ccb(*this, j.first, *j.second);
|
||||
return i;
|
||||
listener.create(*this, j.first, *j.second);
|
||||
}
|
||||
|
||||
void invbind_set::drop_callback(uint64_t handle)
|
||||
void invbind_set::drop_callback(set_listener& listener)
|
||||
{
|
||||
threads::arlock u(get_keymap_lock());
|
||||
auto state = set_internal_t::get_soft(this);
|
||||
if(!state) return;
|
||||
if(state->callbacks.count(handle)) {
|
||||
if(state->callbacks.count(&listener)) {
|
||||
//To avoid races, call DCBs on all factories for this.
|
||||
for(auto j : state->invbinds)
|
||||
state->callbacks[handle].dcb(*this, j.first);
|
||||
state->callbacks.erase(handle);
|
||||
state->pool(handle);
|
||||
listener.destroy(*this, j.first);
|
||||
state->callbacks.erase(&listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue