diff --git a/include/core/settings.hpp b/include/core/settings.hpp index b26ba7b2..3737a9c0 100644 --- a/include/core/settings.hpp +++ b/include/core/settings.hpp @@ -1,6 +1,8 @@ #ifndef _settings__hpp__included__ #define _settings__hpp__included__ +#include "core/window.hpp" + #include #include #include @@ -106,8 +108,22 @@ public: * Get set of all settings. */ static std::set get_settings_set() throw(std::bad_alloc); +/** + * Lock holder + */ + struct lock_holder + { + lock_holder(setting* t) { (targ = t)->mut->lock(); } + ~lock_holder() { targ->mut->unlock(); } + private: + setting* targ; + }; + friend struct lock_holder; protected: std::string settingname; +private: + static setting* get_by_name(const std::string& name); + mutex* mut; }; /** diff --git a/include/core/window.hpp b/include/core/window.hpp index ed225466..af9caf06 100644 --- a/include/core/window.hpp +++ b/include/core/window.hpp @@ -31,6 +31,10 @@ struct mutex * Create a mutex. The returned mutex can be deleted using delete. */ static mutex& aquire() throw(std::bad_alloc); +/** + * Create a recursive mutex. The returned mutex can be deleted using delete. + */ + static mutex& aquire_rec() throw(std::bad_alloc); /** * Destroy a mutex. */ @@ -535,6 +539,9 @@ struct platform static bool pausing_allowed; static double global_volume; + static volatile bool do_exit_dummy_event_loop; + static void dummy_event_loop() throw(); + static void exit_dummy_event_loop() throw(); }; class modal_pause_holder diff --git a/include/platform/wxwidgets/platform.hpp b/include/platform/wxwidgets/platform.hpp index 6d474290..1c04558b 100644 --- a/include/platform/wxwidgets/platform.hpp +++ b/include/platform/wxwidgets/platform.hpp @@ -27,6 +27,7 @@ void bring_app_foreground(); std::string pick_archive_member(wxWindow* parent, const std::string& filename) throw(std::bad_alloc); void boot_emulator(loaded_rom& rom, moviefile& movie); void handle_wx_keyboard(wxKeyEvent& e, bool polarity); +std::string map_keycode_to_key(int kcode); void initialize_wx_keyboard(); void signal_program_exit(); void signal_resize_needed(); diff --git a/src/core/command.cpp b/src/core/command.cpp index 4e85bfaf..080a132d 100644 --- a/src/core/command.cpp +++ b/src/core/command.cpp @@ -1,6 +1,7 @@ #include "core/command.hpp" #include "core/globalwrap.hpp" #include "core/misc.hpp" +#include "core/window.hpp" #include "library/minmax.hpp" #include "library/string.hpp" #include "library/zip.hpp" @@ -14,6 +15,24 @@ namespace std::set command_stack; std::map> aliases; + //Return the recursive mutex. + mutex& cmlock() + { + static mutex& m = mutex::aquire_rec(); + return m; + } + + class cmlock_hold + { + public: + cmlock_hold() { cmlock().lock(); } + ~cmlock_hold() { cmlock().unlock(); } + private: + cmlock_hold(const cmlock_hold& k); + cmlock_hold& operator=(const cmlock_hold& k); + }; + + function_ptr_command run_script("run-script", "run file as a script", "Syntax: run-script \nRuns file just as it would have been entered in the command line\n", [](arg_filename filename) throw(std::bad_alloc, std::runtime_error) { @@ -34,6 +53,7 @@ namespace function_ptr_command<> show_aliases("show-aliases", "show aliases", "Syntax: show-aliases\nShow expansions of all aliases\n", []() throw(std::bad_alloc, std::runtime_error) { + cmlock_hold lck; for(auto i : aliases) for(auto j : i.second) messages << "alias " << i.first << " " << j << std::endl; @@ -45,6 +65,7 @@ namespace auto r = regex("([^ \t]+)[ \t]*", t, "This command only takes one argument"); if(!command::valid_alias_name(r[1])) throw std::runtime_error("Illegal alias name"); + cmlock_hold lck; aliases[r[1]].clear(); messages << "Command '" << r[1] << "' unaliased" << std::endl; }); @@ -56,6 +77,7 @@ namespace auto r = regex("([^ \t]+)[ \t]+([^ \t].*)", t, "Alias name and command needed"); if(!command::valid_alias_name(r[1])) throw std::runtime_error("Illegal alias name"); + cmlock_hold lck; aliases[r[1]].push_back(r[2]); messages << "Command '" << r[1] << "' aliased to '" << r[2] << "'" << std::endl; }); @@ -63,6 +85,7 @@ namespace command::command(const std::string& cmd) throw(std::bad_alloc) { + cmlock_hold lck; if(commands().count(cmd)) std::cerr << "WARNING: Command collision for " << cmd << "!" << std::endl; commands()[commandname = cmd] = this; @@ -70,6 +93,7 @@ command::command(const std::string& cmd) throw(std::bad_alloc) command::~command() throw() { + cmlock_hold lck; commands().erase(commandname); } @@ -79,12 +103,14 @@ void command::invokeC(const std::string& cmd) throw() std::string cmd2 = strip_CR(cmd); if(cmd2 == "?") { //The special ? command. + cmlock_hold lck; for(auto i : commands()) messages << i.first << ": " << i.second->get_short_help() << std::endl; return; } if(firstchar(cmd2) == '?') { //?command. + cmlock_hold lck; std::string rcmd = cmd2.substr(1, min(cmd2.find_first_of(" \t"), cmd2.length())); if(firstchar(rcmd) != '*') { //This may be an alias. @@ -109,21 +135,33 @@ void command::invokeC(const std::string& cmd) throw() may_be_alias_expanded = false; cmd2 = cmd2.substr(1); } - if(may_be_alias_expanded && aliases.count(cmd2)) { - for(auto i : aliases[cmd2]) + //Now this gets painful as command handlers must not be invoked with lock held. + if(may_be_alias_expanded) { + std::list aexp; + { + cmlock_hold lck; + if(!aliases.count(cmd)) + goto not_alias; + aexp = aliases[cmd2]; + } + for(auto i : aexp) invokeC(i); return; } +not_alias: try { size_t split = cmd2.find_first_of(" \t"); std::string rcmd = cmd2.substr(0, min(split, cmd2.length())); std::string args = cmd2.substr(min(cmd2.find_first_not_of(" \t", split), cmd2.length())); command* cmdh = NULL; - if(!commands().count(rcmd)) { - messages << "Unknown command '" << rcmd << "'" << std::endl; - return; + { + cmlock_hold lck; + if(!commands().count(rcmd)) { + messages << "Unknown command '" << rcmd << "'" << std::endl; + return; + } + cmdh = commands()[rcmd]; } - cmdh = commands()[rcmd]; if(command_stack.count(cmd2)) throw std::runtime_error("Recursive command invocation"); command_stack.insert(cmd2); @@ -154,6 +192,7 @@ std::string command::get_long_help() throw(std::bad_alloc) std::set command::get_aliases() throw(std::bad_alloc) { + cmlock_hold lck; std::set r; for(auto i : aliases) r.insert(i.first); @@ -162,6 +201,7 @@ std::set command::get_aliases() throw(std::bad_alloc) std::string command::get_alias_for(const std::string& aname) throw(std::bad_alloc) { + cmlock_hold lck; if(!valid_alias_name(aname)) return ""; if(aliases.count(aname)) { @@ -175,6 +215,7 @@ std::string command::get_alias_for(const std::string& aname) throw(std::bad_allo void command::set_alias_for(const std::string& aname, const std::string& avalue) throw(std::bad_alloc) { + cmlock_hold lck; if(!valid_alias_name(aname)) return; std::list newlist; diff --git a/src/core/framerate.cpp b/src/core/framerate.cpp index d48205f3..33d31303 100644 --- a/src/core/framerate.cpp +++ b/src/core/framerate.cpp @@ -110,6 +110,27 @@ namespace } } + std::pair read() throw() + { + lock_holder lck(this); + return std::make_pair(target_infinite, target_fps); + } + + void set_nominal_framerate(double fps) + { + lock_holder(this); + nominal_rate = fps; + if(target_nominal) { + target_fps = nominal_rate; + target_infinite = false; + } + } + + double get_framerate() + { + lock_holder(this); + return 100.0 * get_realized_fps() / nominal_rate; + } } targetfps; bool turboed = false; @@ -151,16 +172,12 @@ void unfreeze_time(uint64_t curtime) void set_nominal_framerate(double fps) throw() { - nominal_rate = fps; - if(target_nominal) { - target_fps = nominal_rate; - target_infinite = false; - } + targetfps.set_nominal_framerate(fps); } double get_framerate() throw() { - return 100.0 * get_realized_fps() / nominal_rate; + return targetfps.get_framerate(); } void ack_frame_tick(uint64_t usec) throw() @@ -171,22 +188,22 @@ void ack_frame_tick(uint64_t usec) throw() uint64_t to_wait_frame(uint64_t usec) throw() { - if(!frame_number || target_infinite || turboed) + auto target = targetfps.read(); + if(!frame_number || target.first || turboed) return 0; uint64_t lintime = get_time(usec, true); uint64_t frame_lasted = lintime - frame_start_times[0]; - uint64_t frame_should_last = 1000000 / (target_fps * nominal_rate / 100); + uint64_t frame_should_last = 1000000 / (target.second * nominal_rate / 100); if(frame_lasted >= frame_should_last) return 0; //We are late. uint64_t history_frames = min(frame_number, static_cast(HISTORY_FRAMES)); uint64_t history_lasted = lintime - frame_start_times[history_frames - 1]; - uint64_t history_should_last = history_frames * 1000000 / (target_fps * nominal_rate / 100); + uint64_t history_should_last = history_frames * 1000000 / (target.second * nominal_rate / 100); if(history_lasted >= history_should_last) return 0; return min(history_should_last - history_lasted, frame_should_last - frame_lasted); } - uint64_t get_utime() { struct timeval tv; diff --git a/src/core/keymapper.cpp b/src/core/keymapper.cpp index 36165a13..c2f8924a 100644 --- a/src/core/keymapper.cpp +++ b/src/core/keymapper.cpp @@ -78,6 +78,23 @@ namespace globalwrap> modifier_linkages; globalwrap> keygroups; + //Return the recursive mutex. + mutex& kmlock() + { + static mutex& m = mutex::aquire_rec(); + return m; + } + + class kmlock_hold + { + public: + kmlock_hold() { kmlock().lock(); } + ~kmlock_hold() { kmlock().unlock(); } + private: + kmlock_hold(const kmlock_hold& k); + kmlock_hold& operator=(const kmlock_hold& k); + }; + //Returns orig if not linked. const modifier* get_linked_modifier(const modifier* orig) { @@ -90,23 +107,27 @@ namespace modifier::modifier(const std::string& name) throw(std::bad_alloc) { + kmlock_hold lck; known_modifiers()[modname = name] = this; } modifier::modifier(const std::string& name, const std::string& linkgroup) throw(std::bad_alloc) { + kmlock_hold lck; known_modifiers()[modname = name] = this; modifier_linkages()[name] = linkgroup; } modifier::~modifier() throw() { + kmlock_hold lck; known_modifiers().erase(modname); modifier_linkages().erase(modname); } std::set modifier::get_set() throw(std::bad_alloc) { + kmlock_hold lck; std::set r; for(auto i : known_modifiers()) r.insert(i.first); @@ -115,6 +136,7 @@ std::set modifier::get_set() throw(std::bad_alloc) modifier& modifier::lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error) { + kmlock_hold lck; if(!known_modifiers().count(name)) { std::ostringstream x; x << "Invalid modifier '" << name << "'"; @@ -125,11 +147,13 @@ modifier& modifier::lookup(const std::string& name) throw(std::bad_alloc, std::r std::string modifier::name() const throw(std::bad_alloc) { + kmlock_hold lck; return modname; } std::string modifier::linked_name() const throw(std::bad_alloc) { + kmlock_hold lck; const modifier* p = get_linked_modifier(this); if(p == this) return ""; @@ -139,18 +163,21 @@ std::string modifier::linked_name() const throw(std::bad_alloc) void modifier_set::add(const modifier& mod, bool really) throw(std::bad_alloc) { + kmlock_hold lck; if(really) set.insert(&mod); } void modifier_set::remove(const modifier& mod, bool really) throw(std::bad_alloc) { + kmlock_hold lck; if(really) set.erase(&mod); } modifier_set modifier_set::construct(const std::string& _modifiers) throw(std::bad_alloc, std::runtime_error) { + kmlock_hold lck; modifier_set set; std::string modifiers = _modifiers; while(modifiers != "") { @@ -169,6 +196,7 @@ modifier_set modifier_set::construct(const std::string& _modifiers) throw(std::b bool modifier_set::valid(const modifier_set& set, const modifier_set& mask) throw(std::bad_alloc) { + kmlock_hold lck; //No element can be together with its linkage group. for(auto i : set.set) { const modifier* j = get_linked_modifier(i); @@ -191,6 +219,7 @@ bool modifier_set::valid(const modifier_set& set, const modifier_set& mask) thro bool modifier_set::operator==(const modifier_set& m) const throw() { + kmlock_hold lck; for(auto i : set) if(!m.set.count(i)) return false; @@ -202,6 +231,7 @@ bool modifier_set::operator==(const modifier_set& m) const throw() std::ostream& operator<<(std::ostream& os, const modifier_set& m) { + kmlock_hold lck; os << "name() << " "; @@ -212,6 +242,7 @@ std::ostream& operator<<(std::ostream& os, const modifier_set& m) bool modifier_set::triggers(const modifier_set& set, const modifier_set& trigger, const modifier_set& mask) throw(std::bad_alloc) { + kmlock_hold lck; for(auto i : mask.set) { bool ok = false; //OK iff at least one of: @@ -254,16 +285,19 @@ bool modifier_set::triggers(const modifier_set& set, const modifier_set& trigger std::string keygroup::name() throw(std::bad_alloc) { + kmlock_hold lck; return keyname; } const std::string& keygroup::get_class() { + kmlock_hold lck; return clazz; } struct keygroup::parameters keygroup::get_parameters() { + kmlock_hold lck; parameters p; p.ktype = ktype; p.cal_left = cal_left; @@ -276,6 +310,7 @@ struct keygroup::parameters keygroup::get_parameters() std::map keygroup::get_all_parameters() { + kmlock_hold lck; std::map ret; for(auto i : keygroups()) ret[i.first] = i.second->get_parameters(); @@ -284,6 +319,7 @@ std::map keygroup::get_all_parameters( keygroup::keygroup(const std::string& name, const std::string& _clazz, enum type t) throw(std::bad_alloc) { + kmlock_hold lck; keygroups()[keyname = name] = this; clazz = _clazz; ktype = t; @@ -298,11 +334,13 @@ keygroup::keygroup(const std::string& name, const std::string& _clazz, enum type keygroup::~keygroup() throw() { + kmlock_hold lck; keygroups().erase(keyname); } void keygroup::change_type(enum type t) throw() { + kmlock_hold lck; ktype = t; state = 0; if(requests_hook) @@ -311,6 +349,7 @@ void keygroup::change_type(enum type t) throw() void keygroup::request_hook_callback(bool state) { + kmlock_hold lck; requests_hook = state; } @@ -318,6 +357,7 @@ void keygroup::request_hook_callback(bool state) std::pair keygroup::lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error) { + kmlock_hold lck; if(keygroups().count(name)) return std::make_pair(keygroups()[name], 0); std::string prefix = name; @@ -344,6 +384,7 @@ std::pair keygroup::lookup(const std::string& name) throw(s void keygroup::change_calibration(short left, short center, short right, double tolerance) { + kmlock_hold lck; cal_left = left; cal_center = center; cal_right = right; @@ -354,6 +395,7 @@ void keygroup::change_calibration(short left, short center, short right, double double keygroup::compensate(short value) { + kmlock_hold lck; if(ktype == KT_HAT || ktype == KT_KEY || ktype == KT_DISABLED || ktype == KT_MOUSE) return value; //These can't be calibrated. if(value <= cal_left) @@ -370,6 +412,7 @@ double keygroup::compensate(short value) double keygroup::compensate2(double value) { + kmlock_hold lck; switch(ktype) { case KT_DISABLED: case KT_MOUSE: @@ -398,6 +441,7 @@ double keygroup::compensate2(double value) void keygroup::set_position(short pos, const modifier_set& modifiers) throw() { + kmlock_hold lck; last_rawval = pos; if(requests_hook) lua_callback_keyhook(keyname, get_parameters()); @@ -461,26 +505,32 @@ void keygroup::set_position(short pos, const modifier_set& modifiers) throw() void keygroup::run_listeners(const modifier_set& modifiers, unsigned subkey, bool polarity, bool really, double x) { - if(!really) - return; - std::string name = keyname; - if(ktype == KT_AXIS_PAIR && subkey == 0) - name = name + "+"; - if(ktype == KT_AXIS_PAIR && subkey == 1) - name = name + "-"; - if(ktype == KT_HAT && subkey == 0) - name = name + "n"; - if(ktype == KT_HAT && subkey == 1) - name = name + "e"; - if(ktype == KT_HAT && subkey == 2) - name = name + "s"; - if(ktype == KT_HAT && subkey == 3) - name = name + "w"; - information_dispatch::do_key_event(modifiers, *this, subkey, polarity, name); + std::string name; + modifier_set _modifiers = modifiers; + { + if(!really) + return; + kmlock_hold lck; + name = keyname; + if(ktype == KT_AXIS_PAIR && subkey == 0) + name = name + "+"; + if(ktype == KT_AXIS_PAIR && subkey == 1) + name = name + "-"; + if(ktype == KT_HAT && subkey == 0) + name = name + "n"; + if(ktype == KT_HAT && subkey == 1) + name = name + "e"; + if(ktype == KT_HAT && subkey == 2) + name = name + "s"; + if(ktype == KT_HAT && subkey == 3) + name = name + "w"; + } + information_dispatch::do_key_event(_modifiers, *this, subkey, polarity, name); } keygroup* keygroup::lookup_by_name(const std::string& name) throw() { + kmlock_hold lck; if(keygroups().count(name)) return keygroups()[name]; else @@ -489,6 +539,7 @@ keygroup* keygroup::lookup_by_name(const std::string& name) throw() std::set keygroup::get_axis_set() throw(std::bad_alloc) { + kmlock_hold lck; std::set r; for(auto i : keygroups()) { keygroup::parameters p = i.second->get_parameters(); @@ -514,6 +565,7 @@ std::set keygroup::get_axis_set() throw(std::bad_alloc) std::set keygroup::get_keys() throw(std::bad_alloc) { + kmlock_hold lck; std::set r; for(auto i : keygroups()) { switch(i.second->ktype) { @@ -546,12 +598,12 @@ std::set keygroup::get_keys() throw(std::bad_alloc) signed keygroup::get_value() { + kmlock_hold lck; return state; } namespace { - function_ptr_command set_axis("set-axis", "Set mode of Joystick axis", "Syntax: set-axis ...\nKnown options: disabled, axis, axis-inverse, pressure0-\n" "pressure0+, pressure-0, pressure-+, pressure+0, pressure+-\nminus=, zero=, plus=\n" @@ -721,6 +773,7 @@ namespace std::map& keybindings() { + kmlock_hold lck; static std::map x; return x; } @@ -729,35 +782,42 @@ namespace void keymapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command) throw(std::bad_alloc, std::runtime_error) { - triple k(mod, modmask, keyname); - modifier_set _mod = modifier_set::construct(mod); - modifier_set _modmask = modifier_set::construct(modmask); - if(!modifier_set::valid(_mod, _modmask)) - throw std::runtime_error("Invalid modifiers"); - auto g = keygroup::lookup(keyname); - if(!keybindings().count(k)) { - keybindings()[k] = new keybind_data; - keybindings()[k]->mod = _mod; - keybindings()[k]->modmask = _modmask; - keybindings()[k]->group = g.first; - keybindings()[k]->subkey = g.second; + { + kmlock_hold lck; + triple k(mod, modmask, keyname); + modifier_set _mod = modifier_set::construct(mod); + modifier_set _modmask = modifier_set::construct(modmask); + if(!modifier_set::valid(_mod, _modmask)) + throw std::runtime_error("Invalid modifiers"); + auto g = keygroup::lookup(keyname); + if(!keybindings().count(k)) { + keybindings()[k] = new keybind_data; + keybindings()[k]->mod = _mod; + keybindings()[k]->modmask = _modmask; + keybindings()[k]->group = g.first; + keybindings()[k]->subkey = g.second; + } + keybindings()[k]->command = command; } - keybindings()[k]->command = command; inverse_key::notify_update(mod + "/" + modmask + "|" + keyname, command); } void keymapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc, std::runtime_error) { - triple k(mod, modmask, keyname); - if(!keybindings().count(k)) - throw std::runtime_error("Key is not bound"); - delete keybindings()[k]; - keybindings().erase(k); + { + kmlock_hold lck; + triple k(mod, modmask, keyname); + if(!keybindings().count(k)) + throw std::runtime_error("Key is not bound"); + delete keybindings()[k]; + keybindings().erase(k); + } inverse_key::notify_update(mod + "/" + modmask + "|" + keyname, ""); } void keymapper::dumpbindings() throw(std::bad_alloc) { + kmlock_hold lck; for(auto i : keybindings()) { messages << "bind-key "; if(i.first.a != "" || i.first.b != "") @@ -768,6 +828,7 @@ void keymapper::dumpbindings() throw(std::bad_alloc) std::set keymapper::get_bindings() throw(std::bad_alloc) { + kmlock_hold lck; std::set r; for(auto i : keybindings()) r.insert(i.first.a + "/" + i.first.b + "|" + i.first.c); @@ -776,6 +837,7 @@ std::set keymapper::get_bindings() throw(std::bad_alloc) std::string keymapper::get_command_for(const std::string& keyspec) throw(std::bad_alloc) { + kmlock_hold lck; triple k("", "", ""); try { k = parse_to_triple(keyspec); @@ -800,6 +862,7 @@ void keymapper::bind_for(const std::string& keyspec, const std::string& cmd) thr inverse_key::inverse_key(const std::string& command, const std::string& name) throw(std::bad_alloc) { + kmlock_hold lck; cmd = command; oname = name; ikeys().insert(this); @@ -813,44 +876,52 @@ inverse_key::inverse_key(const std::string& command, const std::string& name) th inverse_key::~inverse_key() { + kmlock_hold lck; ikeys().erase(this); forkey().erase(cmd); } std::set inverse_key::get_ikeys() throw(std::bad_alloc) { + kmlock_hold lck; return ikeys(); } std::string inverse_key::getname() throw(std::bad_alloc) { + kmlock_hold lck; return oname; } inverse_key* inverse_key::get_for(const std::string& command) throw(std::bad_alloc) { + kmlock_hold lck; return forkey().count(command) ? forkey()[command] : NULL; } std::set& inverse_key::ikeys() { + kmlock_hold lck; static std::set x; return x; } std::map& inverse_key::forkey() { + kmlock_hold lck; static std::map x; return x; } std::string inverse_key::get(bool primary) throw(std::bad_alloc) { + kmlock_hold lck; return primary ? primary_spec : secondary_spec; } void inverse_key::clear(bool primary) throw(std::bad_alloc) { + kmlock_hold lck; if(primary) { if(primary_spec != "") keymapper::bind_for(primary_spec, ""); @@ -870,6 +941,7 @@ void inverse_key::clear(bool primary) throw(std::bad_alloc) void inverse_key::set(std::string keyspec, bool primary) throw(std::bad_alloc) { + kmlock_hold lck; if(keyspec == "") { clear(primary); return; @@ -891,6 +963,7 @@ void inverse_key::set(std::string keyspec, bool primary) throw(std::bad_alloc) void inverse_key::addkey(const std::string& keyspec) { + kmlock_hold lck; if(primary_spec == "" || primary_spec == keyspec) primary_spec = keyspec; else if(secondary_spec == "") @@ -899,6 +972,7 @@ void inverse_key::addkey(const std::string& keyspec) void inverse_key::notify_update(const std::string& keyspec, const std::string& command) { + kmlock_hold lck; for(auto k : ikeys()) { bool u = false; if(k->primary_spec == keyspec || k->secondary_spec == keyspec) { diff --git a/src/core/moviedata.cpp b/src/core/moviedata.cpp index 6d010470..31025320 100644 --- a/src/core/moviedata.cpp +++ b/src/core/moviedata.cpp @@ -81,6 +81,7 @@ namespace } operator std::string() throw() { + lock_holder lck(this); if(_set) return prefix + "-"; else diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 411dddba..1b5a18fa 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -50,24 +50,55 @@ namespace messages << i << ": " << setting::get(i) << std::endl; } }); + + //Return the mutex. + mutex& stlock() + { + static mutex& m = mutex::aquire(); + return m; + } + + class stlock_hold + { + public: + stlock_hold() { stlock().lock(); } + ~stlock_hold() { stlock().unlock(); } + private: + stlock_hold(const stlock_hold& k); + stlock_hold& operator=(const stlock_hold& k); + }; } setting::setting(const std::string& name) throw(std::bad_alloc) { + stlock_hold lck; + mut = &mutex::aquire(); settings()[settingname = name] = this; } setting::~setting() throw() { + stlock_hold lck; + delete mut; settings().erase(settingname); } +setting* setting::get_by_name(const std::string& name) +{ + stlock_hold lck; + if(!settings().count(name)) + throw std::runtime_error("No such setting '" + name + "'"); + return settings()[name]; +} + void setting::set(const std::string& _setting, const std::string& value) throw(std::bad_alloc, std::runtime_error) { - if(!settings().count(_setting)) - throw std::runtime_error("No such setting '" + _setting + "'"); + setting* tochange = get_by_name(_setting); try { - settings()[_setting]->set(value); + { + lock_holder lck(tochange); + tochange->set(value); + } information_dispatch::do_setting_change(_setting, value); } catch(std::bad_alloc& e) { throw; @@ -83,10 +114,12 @@ bool setting::blank(bool really) throw(std::bad_alloc, std::runtime_error) bool setting::blankable(const std::string& _setting) throw(std::bad_alloc, std::runtime_error) { - if(!settings().count(_setting)) - throw std::runtime_error("No such setting '" + _setting + "'"); + setting* tochange = get_by_name(_setting); try { - return settings()[_setting]->blank(false); + { + lock_holder lck(tochange); + return tochange->blank(false); + } } catch(...) { return false; } @@ -94,11 +127,13 @@ bool setting::blankable(const std::string& _setting) throw(std::bad_alloc, std:: void setting::blank(const std::string& _setting) throw(std::bad_alloc, std::runtime_error) { - if(!settings().count(_setting)) - throw std::runtime_error("No such setting '" + _setting + "'"); + setting* tochange = get_by_name(_setting); try { - if(!settings()[_setting]->blank(true)) - throw std::runtime_error("This setting can't be cleared"); + { + lock_holder lck(tochange); + if(!tochange->blank(true)) + throw std::runtime_error("This setting can't be cleared"); + } information_dispatch::do_setting_clear(_setting); } catch(std::bad_alloc& e) { throw; @@ -109,16 +144,20 @@ void setting::blank(const std::string& _setting) throw(std::bad_alloc, std::runt std::string setting::get(const std::string& _setting) throw(std::bad_alloc, std::runtime_error) { - if(!settings().count(_setting)) - throw std::runtime_error("No such setting '" + _setting + "'"); - return settings()[_setting]->get(); + setting* tochange = get_by_name(_setting); + { + lock_holder lck(tochange); + return tochange->get(); + } } bool setting::is_set(const std::string& _setting) throw(std::bad_alloc, std::runtime_error) { - if(!settings().count(_setting)) - throw std::runtime_error("No such setting '" + _setting + "'"); - return settings()[_setting]->is_set(); + setting* tochange = get_by_name(_setting); + { + lock_holder lck(tochange); + return tochange->is_set(); + } } std::set setting::get_settings_set() throw(std::bad_alloc) @@ -164,6 +203,7 @@ std::string numeric_setting::get() throw(std::bad_alloc) numeric_setting::operator int32_t() throw() { + lock_holder lck(this); return value; } @@ -202,6 +242,7 @@ std::string boolean_setting::get() throw(std::bad_alloc) boolean_setting::operator bool() throw() { + lock_holder lck(this); return value; } @@ -244,5 +285,6 @@ std::string path_setting::get() throw(std::bad_alloc) path_setting::operator std::string() { + lock_holder lck(this); return path; } \ No newline at end of file diff --git a/src/core/window.cpp b/src/core/window.cpp index e2837b08..29ca1096 100644 --- a/src/core/window.cpp +++ b/src/core/window.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -118,6 +120,7 @@ keypress::keypress(modifier_set mod, keygroup& _key, keygroup& _key2, short _val value = _value; } +volatile bool platform::do_exit_dummy_event_loop = false; namespace { @@ -298,6 +301,7 @@ bool platform::is_sound_enabled() throw() void platform::init() { + do_exit_dummy_event_loop = false; msgbuf.register_handler(msg_callback_obj); system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app); time_t curtime = time(NULL); @@ -449,6 +453,24 @@ namespace #define MAXWAIT 100000ULL +void platform::dummy_event_loop() throw() +{ + init_threading(); + while(!do_exit_dummy_event_loop) { + mutex::holder h(*queue_lock); + internal_run_queues(true); + queue_condition->wait(MAXWAIT); + } +} + +void platform::exit_dummy_event_loop() throw() +{ + init_threading(); + do_exit_dummy_event_loop = true; + mutex::holder h(*queue_lock); + queue_condition->signal(); + usleep(200000); +} void platform::flush_command_queue() throw() { diff --git a/src/dummy/thread.cpp b/src/dummy/thread.cpp index 9ce9846b..b12d1065 100644 --- a/src/dummy/thread.cpp +++ b/src/dummy/thread.cpp @@ -24,6 +24,11 @@ mutex& mutex::aquire() throw(std::bad_alloc) return *new dummy_mutex(); } +mutex& mutex::aquire_rec() throw(std::bad_alloc) +{ + return *new dummy_mutex(); +} + struct dummy_condition : public condition { dummy_condition(mutex& m); diff --git a/src/platform/evdev/joystick.cpp b/src/platform/evdev/joystick.cpp index 64dd80e5..ce8e333e 100644 --- a/src/platform/evdev/joystick.cpp +++ b/src/platform/evdev/joystick.cpp @@ -208,9 +208,6 @@ namespace unsigned long axes[(ABS_MAX + div) / div] = {0}; unsigned long evtypes[(EV_MAX + div) / div] = {0}; char namebuffer[256]; - unsigned button_count = 0; - unsigned axis_count = 0; - unsigned hat_count = 0; if(ioctl(fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) { int merrno = errno; messages << "Error probing joystick (evmap; " << filename << "): " << strerror(merrno) @@ -240,13 +237,10 @@ namespace return false; } joystick_create(fd, namebuffer); - for(unsigned i = 0; i <= KEY_MAX; i++) { - if(keys[i / div] & (1ULL << (i % div))) { + for(unsigned i = 0; i <= KEY_MAX; i++) + if(keys[i / div] & (1ULL << (i % div))) joystick_new_button(fd, i, get_button_name(i)); - button_count++; - } - } - for(unsigned i = 0; i <= ABS_MAX; i++) { + for(unsigned i = 0; i <= ABS_MAX; i++) if(axes[i / div] & (1ULL << (i % div))) { if(i < ABS_HAT0X || i > ABS_HAT3Y) { int32_t V[5]; @@ -258,13 +252,9 @@ namespace } joystick_new_axis(fd, i, V[1], V[2], get_axis_name(i), (V[1] < 0) ? keygroup::KT_AXIS_PAIR : keygroup::KT_PRESSURE_MP); - axis_count++; - } else if(i % 2 == 0) { + } else if(i % 2 == 0) joystick_new_hat(fd, i, i + 1, 1, get_axis_name(i), get_axis_name(i + 1)); - hat_count++; - } } - } joystick_message(fd); return true; } diff --git a/src/platform/sdl/thread.cpp b/src/platform/sdl/thread.cpp index 9d16c8f1..57a62cea 100644 --- a/src/platform/sdl/thread.cpp +++ b/src/platform/sdl/thread.cpp @@ -13,6 +13,18 @@ struct sdl_mutex : public mutex SDL_mutex* m; }; +struct sdl_rec_mutex : public mutex +{ + sdl_rec_mutex() throw(std::bad_alloc); + ~sdl_rec_mutex() throw(); + void lock() throw(); + void unlock() throw(); + SDL_mutex* m; + volatile bool locked; + uint32_t owner; + uint32_t count; +}; + sdl_mutex::sdl_mutex() throw(std::bad_alloc) { m = SDL_CreateMutex(); @@ -25,6 +37,21 @@ sdl_mutex::~sdl_mutex() throw() SDL_DestroyMutex(m); } +sdl_rec_mutex::sdl_rec_mutex() throw(std::bad_alloc) +{ + m = SDL_CreateMutex(); + if(!m) + throw std::bad_alloc(); + locked = false; + owner = 0; + count = 0; +} + +sdl_rec_mutex::~sdl_rec_mutex() throw() +{ + SDL_DestroyMutex(m); +} + void sdl_mutex::lock() throw() { SDL_mutexP(m); @@ -35,11 +62,42 @@ void sdl_mutex::unlock() throw() SDL_mutexV(m); } +void sdl_rec_mutex::lock() throw() +{ + uint32_t our_id = SDL_ThreadID(); + if(locked && owner == our_id) { + //Owned by us, increment lock count. + ++count; + return; + } + SDL_mutexP(m); + locked = true; + owner = our_id; + count = 1; +} + +void sdl_rec_mutex::unlock() throw() +{ + uint32_t our_id = SDL_ThreadID(); + if(!locked || owner != our_id) + std::cerr << "Warning: Trying to unlock recursive lock locked by another thread!" << std::endl; + if(!--count) { + locked = false; + owner = 0; + SDL_mutexV(m); + } +} + mutex& mutex::aquire() throw(std::bad_alloc) { return *new sdl_mutex; } +mutex& mutex::aquire_rec() throw(std::bad_alloc) +{ + return *new sdl_rec_mutex; +} + struct sdl_condition : public condition { sdl_condition(mutex& m) throw(std::bad_alloc); diff --git a/src/platform/wxwidgets/keyboard.cpp b/src/platform/wxwidgets/keyboard.cpp index 2642b806..44dda8c8 100644 --- a/src/platform/wxwidgets/keyboard.cpp +++ b/src/platform/wxwidgets/keyboard.cpp @@ -283,6 +283,17 @@ namespace } } +std::string map_keycode_to_key(int kcode) +{ + key_entry* k = keys; + while(k->name) { + if(k->keynum == kcode) + return k->name; + k++; + } + return ""; +} + void handle_wx_keyboard(wxKeyEvent& e, bool polarity) { int mods = e.GetModifiers(); diff --git a/src/platform/wxwidgets/main.cpp b/src/platform/wxwidgets/main.cpp index 451bd169..370e4363 100644 --- a/src/platform/wxwidgets/main.cpp +++ b/src/platform/wxwidgets/main.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #define UISERV_RESIZED 9991 #define UISERV_UIFUN 9992 @@ -232,6 +233,13 @@ end: std::ofstream lsave(get_config_path() + "/" + our_rom_name + ".ls"); lsave << last_save; } + + + void* eloop_helper(void* x) + { + platform::dummy_event_loop(); + return NULL; + } } wxString towxstring(const std::string& str) throw(std::bad_alloc) @@ -287,18 +295,53 @@ void graphics_plugin::quit() throw() { } +static const wxCmdLineEntryDesc dummy_descriptor_table[] = { + { wxCMD_LINE_PARAM, NULL, NULL, NULL, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | + wxCMD_LINE_PARAM_MULTIPLE }, + { wxCMD_LINE_NONE } +}; class lsnes_app : public wxApp { public: + lsnes_app(); virtual bool OnInit(); virtual int OnExit(); + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); +private: + bool settings_mode; }; IMPLEMENT_APP(lsnes_app) +lsnes_app::lsnes_app() +{ + settings_mode = false; +} + +void lsnes_app::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.SetDesc(dummy_descriptor_table); + parser.SetSwitchChars(wxT("")); +} + +bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser) +{ + std::vector cmdline; + for(size_t i = 0; i< parser.GetParamCount(); i++) + cmdline.push_back(tostdstring(parser.GetParam(i))); + for(auto i: cmdline) { + if(i == "--settings") + settings_mode = true; + } +} + + bool lsnes_app::OnInit() { + wxApp::OnInit(); + reached_main(); set_random_seed(); bring_app_foreground(); @@ -309,7 +352,6 @@ bool lsnes_app::OnInit() ui_thread = &thread_id::me(); platform::init(); - init_lua(); messages << "BSNES version: " << emucore_get_version() << std::endl; messages << "lsnes version: lsnes rr" << lsnes_version << std::endl; @@ -323,6 +365,21 @@ bool lsnes_app::OnInit() command::invokeC("run-script " + cfgpath + "/lsneswxw.rc"); messages << "--- End running lsnesrc --- " << std::endl; + if(settings_mode) { + //We got to boot this up quite a bit to get the joystick driver working. + //In practicular, we need joystick thread and emulator thread in pause. + joystick_thread_handle = &thread::create(joystick_thread, NULL); + thread* dummy_loop = &thread::create(eloop_helper, NULL); + wxsetingsdialog_display(NULL); + platform::exit_dummy_event_loop(); + joystick_plugin::signal(); + joystick_thread_handle->join(); + dummy_loop->join(); + save_configuration(); + return false; + } + init_lua(); + joystick_thread_handle = &thread::create(joystick_thread, NULL); msg_window = new wxwin_messages(); @@ -335,12 +392,14 @@ bool lsnes_app::OnInit() int lsnes_app::OnExit() { + if(settings_mode) + return 0; //NULL these so no further messages will be sent. msg_window = NULL; main_window = NULL; + save_configuration(); information_dispatch::do_dump_end(); rrdata::close(); - save_configuration(); joystick_plugin::signal(); joystick_thread_handle->join(); platform::quit(); diff --git a/src/platform/wxwidgets/mainwindow.cpp b/src/platform/wxwidgets/mainwindow.cpp index 88ddb48a..ae654cd2 100644 --- a/src/platform/wxwidgets/mainwindow.cpp +++ b/src/platform/wxwidgets/mainwindow.cpp @@ -210,9 +210,9 @@ namespace { std::string v = (stringfmt() << target).str(); if(target < 0) - runemufn([]() { setting::set("targetfps", "infinite"); }); + setting::set("targetfps", "infinite"); else - runemufn([v]() { setting::set("targetfps", v); }); + setting::set("targetfps", v); } class controller_autohold_menu : public wxMenu @@ -452,9 +452,7 @@ namespace std::string movie_path() { - std::string x; - runemufn([&x]() { x = setting::get("moviepath"); }); - return x; + return setting::get("moviepath"); } } @@ -962,13 +960,14 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e) return; } case wxID_SET_SPEED: { - std::string value; bool bad = false; - runemufn([&value]() { value = setting::is_set("targetfps") ? setting::get("targetfps") : ""; }); + std::string value = setting::is_set("targetfps") ? setting::get("targetfps") : ""; value = pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value); - runemufn([&bad, &value]() { try { setting::set("targetfps", value); } catch(...) { bad = true; } }); - if(bad) + try { + setting::set("targetfps", value); + } catch(...) { wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this); + } return; } case wxID_SET_VOLUME: { diff --git a/src/platform/wxwidgets/settings.cpp b/src/platform/wxwidgets/settings.cpp index e80aa104..d8445abb 100644 --- a/src/platform/wxwidgets/settings.cpp +++ b/src/platform/wxwidgets/settings.cpp @@ -46,6 +46,154 @@ const char* scalealgo_choices[] = {"Fast Bilinear", "Bilinear", "Bicubic", "Expe namespace { + class wxdialog_pressbutton; + volatile bool keygrab_active = false; + std::string pkey; + wxdialog_pressbutton* presser = NULL; + + void report_grab_key(const std::string& name); + + class keygrabber : public information_dispatch + { + public: + keygrabber() : information_dispatch("wxwdigets-key-grabber") { keygrab_active = false; } + void on_key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey, + bool polarity, const std::string& name) + { + if(!keygrab_active) + return; + if(polarity) + pkey = name; + else { + if(pkey == name) { + keygrab_active = false; + runuifun([pkey]() { report_grab_key(pkey); }); + } else + pkey = ""; + } + } + } keygrabber; + + class wxdialog_pressbutton : public wxDialog + { + public: + wxdialog_pressbutton(wxWindow* parent, const std::string& title); + std::string getkey() { return key; } + void on_mouse(wxMouseEvent& e); + void on_keyboard_up(wxKeyEvent& e); + void on_keyboard_down(wxKeyEvent& e); + void dismiss_with(const std::string& k); + private: + bool handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const, bool(wxMouseEvent::*up)()const, + const std::string& k, int flag); + std::string key; + int mouseflag; + int lastkbdkey; + }; + + void report_grab_key(const std::string& name) + { + presser->dismiss_with(name); + } + + int vert_padding = 40; + int horiz_padding = 60; + + wxdialog_pressbutton::wxdialog_pressbutton(wxWindow* parent, const std::string& title) + : wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1)) + { + wxStaticText* t; + wxBoxSizer* s2 = new wxBoxSizer(wxVERTICAL); + wxPanel* p = new wxPanel(this, wxID_ANY); + s2->Add(p, 1, wxGROW); + lastkbdkey = -1; + mouseflag = 0; + Centre(); + wxFlexGridSizer* s = new wxFlexGridSizer(3, 3, 0, 0); + p->SetSizer(s); + SetSizer(s2); + s->Add(horiz_padding, vert_padding); + s->Add(0, 0); + s->Add(0, 0); + s->Add(0, 0); + s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign")), 1, wxGROW); + s->Add(0, 0); + s->Add(0, 0); + s->Add(0, 0); + s->Add(horiz_padding, vert_padding); + p->SetFocus(); + p->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_down), NULL, this); + p->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_up), NULL, this); + p->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + p->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + p->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + p->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + p->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + p->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_down), NULL, this); + t->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_up), NULL, this); + t->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + t->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this); + presser = this; + keygrab_active = true; + s->SetSizeHints(this); + Fit(); + } + + bool wxdialog_pressbutton::handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const, + bool(wxMouseEvent::*up)()const, const std::string& k, int flag) + { + if((e.*down)()) + mouseflag = flag; + if((e.*up)()) { + if(mouseflag == flag) { + dismiss_with(k); + return true; + } else + mouseflag = 0; + } + return false; + } + + void wxdialog_pressbutton::on_mouse(wxMouseEvent& e) + { + handle_mousebtn(e, &wxMouseEvent::LeftDown, &wxMouseEvent::LeftUp, "mouse_left", 1); + handle_mousebtn(e, &wxMouseEvent::MiddleDown, &wxMouseEvent::MiddleUp, "mouse_center", 2); + handle_mousebtn(e, &wxMouseEvent::RightDown, &wxMouseEvent::RightUp, "mouse_right", 3); + } + + void wxdialog_pressbutton::on_keyboard_down(wxKeyEvent& e) + { + lastkbdkey = e.GetKeyCode(); + mouseflag = 0; + } + + void wxdialog_pressbutton::on_keyboard_up(wxKeyEvent& e) + { + int kcode = e.GetKeyCode(); + if(lastkbdkey == kcode) { + dismiss_with(map_keycode_to_key(kcode)); + } else { + lastkbdkey = -1; + mouseflag = 0; + } + } + + void wxdialog_pressbutton::dismiss_with(const std::string& k) + { + if(k == "") + return; + if(key == "") { + keygrab_active = false; + key = k; + EndModal(wxID_OK); + } + } + struct keyentry_mod_data { wxCheckBox* pressed; @@ -62,6 +210,7 @@ namespace void on_ok(wxCommandEvent& e); void on_cancel(wxCommandEvent& e); void on_clear(wxCommandEvent& e); + void on_pressbutton(wxCommandEvent& e); void on_classchange(wxCommandEvent& e); std::string getkey(); private: @@ -73,11 +222,13 @@ namespace void set_class(const std::string& _class); std::map modifiers; std::map> classes; + std::string wtitle; std::string currentclass; wxFlexGridSizer* top_s; wxFlexGridSizer* t_s; wxComboBox* mainclass; wxComboBox* mainkey; + wxButton* press; wxButton* ok; wxButton* cancel; wxButton* clear; @@ -92,19 +243,19 @@ namespace wxString emptystring; std::set mods, keys; + wtitle = title; + cleared = false; - runemufn([&mods, &keys, &classes, &classeslist]() { - std::set x; - mods = modifier::get_set(); - keys = keygroup::get_keys(); - for(auto i : keys) { - std::string kclass = keygroup::lookup(i).first->get_class(); - if(!x.count(kclass)) - classeslist.push_back(towxstring(kclass)); - x.insert(kclass); - classes[kclass].insert(i); - } - }); + std::set x; + mods = modifier::get_set(); + keys = keygroup::get_keys(); + for(auto i : keys) { + std::string kclass = keygroup::lookup(i).first->get_class(); + if(!x.count(kclass)) + classeslist.push_back(towxstring(kclass)); + x.insert(kclass); + classes[kclass].insert(i); + } Centre(); top_s = new wxFlexGridSizer(2, 1, 0, 0); @@ -135,6 +286,9 @@ namespace top_s->Add(t_s); wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL); + pbutton_s->Add(press = new wxButton(this, wxID_OK, wxT("Prompt key")), 0, wxGROW); + press->Connect(wxEVT_COMMAND_BUTTON_CLICKED, + wxCommandEventHandler(wxdialog_keyentry::on_pressbutton), NULL, this); if(clearable) pbutton_s->Add(clear = new wxButton(this, wxID_OK, wxT("Clear")), 0, wxGROW); pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW); @@ -222,9 +376,11 @@ namespace for(auto i : classes) if(i.second.count(key)) _class = i.first; - set_class(_class); - mainclass->SetValue(towxstring(_class)); - mainkey->SetValue(towxstring(key)); + if(_class != "") { + set_class(_class); + mainclass->SetValue(towxstring(_class)); + mainkey->SetValue(towxstring(key)); + } t_s->Layout(); top_s->Layout(); Fit(); @@ -281,6 +437,23 @@ namespace } } + void wxdialog_keyentry::on_pressbutton(wxCommandEvent& e) + { + wxdialog_pressbutton* p = new wxdialog_pressbutton(this, wtitle); + p->ShowModal(); + std::string key = p->getkey(); + p->Destroy(); + std::string _class; + for(auto i : classes) + if(i.second.count(key)) + _class = i.first; + if(_class == "") + return; + set_class(_class); + mainclass->SetValue(towxstring(_class)); + mainkey->SetValue(towxstring(key)); + } + void wxdialog_keyentry::on_ok(wxCommandEvent& e) { EndModal(wxID_OK); @@ -384,11 +557,9 @@ wxeditor_esettings_joystick_aconfig::wxeditor_esettings_joystick_aconfig(wxWindo aname = _aname; keygroup::parameters params; - runemufn([aname, ¶ms]() { - auto k = keygroup::lookup_by_name(aname); - if(k) - params = k->get_parameters(); - }); + auto k = keygroup::lookup_by_name(aname); + if(k) + params = k->get_parameters(); switch(params.ktype) { case keygroup::KT_DISABLED: didx = 0; break; @@ -456,12 +627,10 @@ void wxeditor_esettings_joystick_aconfig::on_ok(wxCommandEvent& e) double ntol; keygroup* k; - runemufn([&k, aname, &_ctype]() { - k = keygroup::lookup_by_name(aname); - if(k) - _ctype = k->get_parameters().ktype; - }); - if(!k) { + k = keygroup::lookup_by_name(aname); + if(k) + _ctype = k->get_parameters().ktype; + else { //Axis gone away? EndModal(wxID_OK); return; @@ -502,11 +671,9 @@ void wxeditor_esettings_joystick_aconfig::on_ok(wxCommandEvent& e) return; } - runemufn([&k, _ctype, _ntype, nlow, nmid, nhi, ntol]() { - if(_ctype != _ntype) - k->change_type(_ntype); - k->change_calibration(nlow, nmid, nhi, ntol); - }); + if(_ctype != _ntype) + k->change_type(_ntype); + k->change_calibration(nlow, nmid, nhi, ntol); EndModal(wxID_OK); } @@ -534,7 +701,8 @@ namespace { std::string formattype(keygroup::type t) { - if(t == keygroup::KT_AXIS_PAIR) return AMODE_AXIS_PAIR; + if(t == keygroup::KT_DISABLED) return AMODE_DISABLED; + else if(t == keygroup::KT_AXIS_PAIR) return AMODE_AXIS_PAIR; else if(t == keygroup::KT_AXIS_PAIR_INVERSE) return AMODE_AXIS_PAIR_INVERSE; else if(t == keygroup::KT_PRESSURE_0M) return AMODE_PRESSURE_0M; else if(t == keygroup::KT_PRESSURE_0P) return AMODE_PRESSURE_0P; @@ -591,14 +759,12 @@ void wxeditor_esettings_joystick::refresh() { //Collect the new settings. std::map x; - runemufn([&x]() { - auto axisnames = keygroup::get_axis_set(); - for(auto i : axisnames) { - keygroup* k = keygroup::lookup_by_name(i); - if(k) - x[i] = k->get_parameters(); - } - }); + auto axisnames = keygroup::get_axis_set(); + for(auto i : axisnames) { + keygroup* k = keygroup::lookup_by_name(i); + if(k) + x[i] = k->get_parameters(); + } unsigned jcount = 0; for(auto i : x) { @@ -709,8 +875,7 @@ void wxeditor_esettings_paths::on_configure(wxCommandEvent& e) name = SLOTPATH; else return; - std::string val; - runemufn([&val, name]() { val = setting::get(name); }); + std::string val = setting::get(name); try { if(e.GetId() == wxID_HIGHEST + 4) val = pick_text(this, "Change number of slots", "Enter number of slots:", val); @@ -721,22 +886,22 @@ void wxeditor_esettings_paths::on_configure(wxCommandEvent& e) return; } std::string err; - runemufn([val, name, &err]() { try { setting::set(name, val); } catch(std::exception& e) { err = e.what(); }}); - if(err != "") + try { + setting::set(name, val); + } catch(std::exception& e) { wxMessageBox(wxT("Invalid value"), wxT("Can't change value"), wxICON_EXCLAMATION | wxOK); + } refresh(); } void wxeditor_esettings_paths::refresh() { std::string rpath, fpath, spath, nslot, lpath; - runemufn([&rpath, &fpath, &spath, &nslot, &lpath]() { - fpath = setting::get(FIRMWAREPATH); - rpath = setting::get(ROMPATH); - spath = setting::get(MOVIEPATH); - nslot = setting::get(SAVESLOTS); - lpath = setting::get(SLOTPATH); - }); + fpath = setting::get(FIRMWAREPATH); + rpath = setting::get(ROMPATH); + spath = setting::get(MOVIEPATH); + nslot = setting::get(SAVESLOTS); + lpath = setting::get(SLOTPATH); rompath->SetLabel(towxstring(rpath)); firmpath->SetLabel(towxstring(fpath)); savepath->SetLabel(towxstring(spath)); @@ -929,11 +1094,10 @@ void wxeditor_esettings_aliases::on_add(wxCommandEvent& e) show_message_ok(this, "Error", "Not a valid alias name: " + name, wxICON_EXCLAMATION); throw canceled_exception(); } - std::string old_alias_value; - runemufn([name, &old_alias_value]() { old_alias_value = command::get_alias_for(name); }); + std::string old_alias_value = command::get_alias_for(name); std::string newcmd = pick_text(this, "Edit alias", "Enter new commands for '" + name + "':", old_alias_value, true); - runemufn([name, newcmd]() { command::set_alias_for(name, newcmd); }); + command::set_alias_for(name, newcmd); } catch(...) { } refresh(); @@ -947,11 +1111,10 @@ void wxeditor_esettings_aliases::on_edit(wxCommandEvent& e) return; } try { - std::string old_alias_value; - runemufn([name, &old_alias_value]() { old_alias_value = command::get_alias_for(name); }); + std::string old_alias_value = command::get_alias_for(name); std::string newcmd = pick_text(this, "Edit alias", "Enter new commands for '" + name + "':", old_alias_value, true); - runemufn([name, newcmd]() { command::set_alias_for(name, newcmd); }); + command::set_alias_for(name, newcmd); } catch(...) { } refresh(); @@ -964,7 +1127,7 @@ void wxeditor_esettings_aliases::on_delete(wxCommandEvent& e) refresh(); return; } - runemufn([name]() { command::set_alias_for(name, ""); }); + command::set_alias_for(name, ""); refresh(); } @@ -973,7 +1136,7 @@ void wxeditor_esettings_aliases::refresh() int n = select->GetSelection(); std::set bind; std::vector choices; - runemufn([&bind]() { bind = command::get_aliases(); }); + bind = command::get_aliases(); for(auto i : bind) { numbers[choices.size()] = i; choices.push_back(towxstring(i)); @@ -1007,6 +1170,7 @@ public: void on_primary(wxCommandEvent& e); void on_secondary(wxCommandEvent& e); void on_change(wxCommandEvent& e); + void kill_internal_data(); private: std::map items; std::map realitems; @@ -1060,6 +1224,14 @@ wxeditor_esettings_hotkeys::~wxeditor_esettings_hotkeys() { } +void wxeditor_esettings_hotkeys::kill_internal_data() +{ + items.clear(); + realitems.clear(); + leafname.clear(); + true_root = wxTreeItemId(); +} + void wxeditor_esettings_hotkeys::on_primary(wxCommandEvent& e) { std::string name = selected(); @@ -1068,13 +1240,12 @@ void wxeditor_esettings_hotkeys::on_primary(wxCommandEvent& e) return; } try { - std::string key; inverse_key* ik = realitems[name]; if(!ik) { refresh(); return; } - runemufn([&key, ik]() { key = ik->get(true); }); + std::string key = ik->get(true); wxdialog_keyentry* d = new wxdialog_keyentry(this, "Specify key for " + name, key, true); if(d->ShowModal() == wxID_CANCEL) { d->Destroy(); @@ -1083,9 +1254,9 @@ void wxeditor_esettings_hotkeys::on_primary(wxCommandEvent& e) key = d->getkey(); d->Destroy(); if(key != "") - runemufn([key, ik]() { ik->set(key, true); }); + ik->set(key, true); else - runemufn([key, ik]() { ik->clear(true); }); + ik->clear(true); refresh(); } catch(...) { refresh(); @@ -1100,13 +1271,12 @@ void wxeditor_esettings_hotkeys::on_secondary(wxCommandEvent& e) return; } try { - std::string key; inverse_key* ik = realitems[name]; if(!ik) { refresh(); return; } - runemufn([&key, ik]() { key = ik->get(false); }); + std::string key = ik->get(false); wxdialog_keyentry* d = new wxdialog_keyentry(this, "Specify key for " + name, key, true); if(d->ShowModal() == wxID_CANCEL) { d->Destroy(); @@ -1115,9 +1285,9 @@ void wxeditor_esettings_hotkeys::on_secondary(wxCommandEvent& e) key = d->getkey(); d->Destroy(); if(key != "") - runemufn([key, ik]() { ik->set(key, false); }); + ik->set(key, false); else - runemufn([key, ik]() { ik->clear(false); }); + ik->clear(false); refresh(); } catch(...) { refresh(); @@ -1129,13 +1299,11 @@ void wxeditor_esettings_hotkeys::refresh() std::set closure_additional; std::map keyorder; std::map> data; - runemufn([&data, &keyorder]() { - auto x = inverse_key::get_ikeys(); - for(auto y : x) { - keyorder[y->getname()] = y; - data[y] = std::make_pair(y->get(true), y->get(false)); - } - }); + auto x = inverse_key::get_ikeys(); + for(auto y : x) { + keyorder[y->getname()] = y; + data[y] = std::make_pair(y->get(true), y->get(false)); + } //Close keyorder with respect to parents. for(auto i : keyorder) { std::string tmp = i.first; @@ -1282,22 +1450,15 @@ void wxeditor_esettings_bindings::on_add(wxCommandEvent& e) d->Destroy(); std::string newcommand = pick_text(this, "New binding", "Enter command for binding:", ""); - bool fault = false; - std::string faulttext; - runemufn([&fault, &faulttext, name, newcommand]() { - try { - keymapper::bind_for(name, newcommand); - } catch(std::exception& e) { - fault = true; - faulttext = e.what(); - } - }); - if(fault) - wxMessageBox(wxT("Error"), towxstring("Can't bind key: " + faulttext), wxICON_EXCLAMATION); - refresh(); + try { + keymapper::bind_for(name, newcommand); + } catch(std::exception& e) { + wxMessageBox(wxT("Error"), towxstring(std::string("Can't bind key: ") + e.what()), + wxICON_EXCLAMATION); + } } catch(...) { - refresh(); } + refresh(); } void wxeditor_esettings_bindings::on_edit(wxCommandEvent& e) @@ -1308,26 +1469,18 @@ void wxeditor_esettings_bindings::on_edit(wxCommandEvent& e) return; } try { - std::string old_command_value; - runemufn([&old_command_value, name]() { old_command_value = keymapper::get_command_for(name); }); + std::string old_command_value = keymapper::get_command_for(name); std::string newcommand = pick_text(this, "Edit binding", "Enter new command for binding:", old_command_value); - bool fault = false; - std::string faulttext; - runemufn([&fault, &faulttext, name, newcommand]() { - try { - keymapper::bind_for(name, newcommand); - } catch(std::exception& e) { - fault = true; - faulttext = e.what(); - } - }); - if(fault) - wxMessageBox(wxT("Error"), towxstring("Can't bind key: " + faulttext), wxICON_EXCLAMATION); - refresh(); + try { + keymapper::bind_for(name, newcommand); + } catch(std::exception& e) { + wxMessageBox(wxT("Error"), towxstring(std::string("Can't bind key: ") + e.what()), + wxICON_EXCLAMATION); + } } catch(...) { - refresh(); } + refresh(); } void wxeditor_esettings_bindings::on_delete(wxCommandEvent& e) @@ -1337,7 +1490,7 @@ void wxeditor_esettings_bindings::on_delete(wxCommandEvent& e) refresh(); return; } - runemufn([name]() { try { keymapper::bind_for(name, ""); } catch(...) {} }); + try { keymapper::bind_for(name, ""); } catch(...) {} refresh(); } @@ -1346,11 +1499,9 @@ void wxeditor_esettings_bindings::refresh() int n = select->GetSelection(); std::map bind; std::vector choices; - runemufn([&bind]() { - std::set a = keymapper::get_bindings(); - for(auto i : a) - bind[i] = keymapper::get_command_for(i); - }); + std::set a = keymapper::get_bindings(); + for(auto i : a) + bind[i] = keymapper::get_command_for(i); for(auto i : bind) { numbers[choices.size()] = i.first; choices.push_back(towxstring(i.first + " (" + i.second + ")")); @@ -1439,17 +1590,17 @@ void wxeditor_esettings_advanced::on_change(wxCommandEvent& e) return; std::string value; std::string err; - runemufn([name, &value]() { value = setting::get(name); }); + value = setting::get(name); try { value = pick_text(this, "Set value to", "Set " + name + " to value:", value); } catch(...) { return; } - runemufn([name, value, &err]() { - try { setting::set(name, value); } catch(std::exception& e) { err = e.what(); } - }); - if(err != "") + try { + setting::set(name, value); + } catch(std::exception& e) { wxMessageBox(towxstring(err), wxT("Error setting value"), wxICON_EXCLAMATION | wxOK); + } } void wxeditor_esettings_advanced::on_selchange(wxCommandEvent& e) @@ -1466,9 +1617,11 @@ void wxeditor_esettings_advanced::on_clear(wxCommandEvent& e) if(name == "") return; bool err = false; - runemufn([name, &err]() { try { setting::blank(name); } catch(...) { err = true; }}); - if(err) + try { + setting::blank(name); + } catch(...) { wxMessageBox(wxT("This setting can't be cleared"), wxT("Error"), wxICON_EXCLAMATION | wxOK); + } } void wxeditor_esettings_advanced::on_setting_change(const std::string& setting, const std::string& value) @@ -1489,16 +1642,14 @@ void wxeditor_esettings_advanced::on_setting_clear(const std::string& setting) void wxeditor_esettings_advanced::refresh() { - runemufn([&settings, &values, &blankables]() { - settings = setting::get_settings_set(); - blankables.clear(); - for(auto i : settings) { - if(setting::is_set(i)) - values[i] = setting::get(i); - if(setting::blankable(i)) - blankables.insert(i); - } - }); + settings = setting::get_settings_set(); + blankables.clear(); + for(auto i : settings) { + if(setting::is_set(i)) + values[i] = setting::get(i); + if(setting::blankable(i)) + blankables.insert(i); + } _refresh(); } @@ -1537,11 +1688,15 @@ private: wxWindow* joystick_window; wxNotebook* tabset; wxButton* closebutton; + wxeditor_esettings_hotkeys* hotkeytab; }; wxeditor_esettings::wxeditor_esettings(wxWindow* parent) : wxDialog(parent, wxID_ANY, wxT("lsnes: Configure emulator"), wxDefaultPosition, wxSize(-1, -1)) { + //Grab keys to prevent the joystick driver from running who knows what commands. + keygrabber.grab_keys(); + Centre(); wxSizer* top_s = new wxBoxSizer(wxVERTICAL); SetSizer(top_s); @@ -1550,7 +1705,7 @@ wxeditor_esettings::wxeditor_esettings(wxWindow* parent) tabset->AddPage(new wxeditor_esettings_joystick(tabset), wxT("Joysticks")); tabset->AddPage(new wxeditor_esettings_paths(tabset), wxT("Paths")); tabset->AddPage(new wxeditor_esettings_screen(tabset), wxT("Scaling")); - tabset->AddPage(new wxeditor_esettings_hotkeys(tabset), wxT("Hotkeys")); + tabset->AddPage(hotkeytab = new wxeditor_esettings_hotkeys(tabset), wxT("Hotkeys")); tabset->AddPage(new wxeditor_esettings_aliases(tabset), wxT("Aliases")); tabset->AddPage(new wxeditor_esettings_bindings(tabset), wxT("Bindings")); tabset->AddPage(new wxeditor_esettings_advanced(tabset), wxT("Advanced")); @@ -1578,6 +1733,8 @@ bool wxeditor_esettings::ShouldPreventAppExit() const void wxeditor_esettings::on_close(wxCommandEvent& e) { + hotkeytab->kill_internal_data(); + keygrabber.ungrab_keys(); EndModal(wxID_OK); } diff --git a/src/platform/wxwidgets/thread.cpp b/src/platform/wxwidgets/thread.cpp index 88d89fa4..66e1c7ea 100644 --- a/src/platform/wxwidgets/thread.cpp +++ b/src/platform/wxwidgets/thread.cpp @@ -11,6 +11,18 @@ struct wxw_mutex : public mutex wxMutex* m; }; +struct wxw_rec_mutex : public mutex +{ + wxw_rec_mutex() throw(std::bad_alloc); + ~wxw_rec_mutex() throw(); + void lock() throw(); + void unlock() throw(); + wxMutex* m; + volatile bool locked; + uint32_t owner; + uint32_t count; +}; + wxw_mutex::wxw_mutex() throw(std::bad_alloc) { m = new wxMutex(); @@ -31,11 +43,55 @@ void wxw_mutex::unlock() throw() m->Unlock(); } +wxw_rec_mutex::wxw_rec_mutex() throw(std::bad_alloc) +{ + m = new wxMutex(); + locked = false; + owner = 0; + count = 0; +} + +wxw_rec_mutex::~wxw_rec_mutex() throw() +{ + delete m; +} + +void wxw_rec_mutex::lock() throw() +{ + uint32_t our_id = wxThread::GetCurrentId(); + if(locked && owner == our_id) { + //Owned by us, increment lock count. + ++count; + return; + } + m->Lock(); + locked = true; + owner = our_id; + count = 1; +} + +void wxw_rec_mutex::unlock() throw() +{ + uint32_t our_id = wxThread::GetCurrentId(); + if(!locked || owner != our_id) + std::cerr << "Warning: Trying to unlock recursive lock locked by another thread!" << std::endl; + if(!--count) { + locked = false; + owner = 0; + m->Unlock(); + } +} + mutex& mutex::aquire() throw(std::bad_alloc) { return *new wxw_mutex; } +mutex& mutex::aquire_rec() throw(std::bad_alloc) +{ + return *new wxw_rec_mutex; +} + struct wxw_condition : public condition { wxw_condition(mutex& m) throw(std::bad_alloc);