Wxwidgets: Allow controlling dumper start/stop from menu

This commit is contained in:
Ilari Liusvaara 2012-01-11 01:21:13 +02:00
parent 34a5e281ca
commit 8df91c55f2
19 changed files with 646 additions and 103 deletions

View file

@ -1 +1 @@
1-β3
1-β4

View file

@ -0,0 +1,87 @@
#ifndef _advdumper__hpp__included__
#define _advdumper__hpp__included__
#include <string>
#include <set>
#include <stdexcept>
class adv_dumper
{
public:
/**
* Register a dumper.
*
* Parameter id: The ID of dumper.
* Throws std::bad_alloc: Not enough memory.
*/
adv_dumper(const std::string& id) throw(std::bad_alloc);
/**
* Unregister a dumper.
*/
~adv_dumper();
/**
* Get ID of dumper.
*
* Returns: The id.
*/
const std::string& id() throw();
/**
* Get set of all dumpers.
*
* Returns: The set.
* Throws std::bad_alloc: Not enough memory.
*/
static std::set<adv_dumper*> get_dumper_set() throw(std::bad_alloc);
/**
* List all valid submodes.
*
* Returns: List of all valid submodes. Empty list means this dumper has no submodes.
* Throws std::bad_alloc: Not enough memory.
*/
virtual std::set<std::string> list_submodes() throw(std::bad_alloc) = 0;
/**
* Does this dumper want a prefix?
*
* parameter mode: The submode.
*/
virtual bool wants_prefix(const std::string& mode) throw() = 0;
/**
* Get human-readable name for this dumper.
*
* Returns: The name.
* Throws std::bad_alloc: Not enough memory.
*/
virtual std::string name() throw(std::bad_alloc) = 0;
/**
* Get human-readable name for submode.
*
* Parameter mode: The submode.
* Returns: The name.
* Throws std::bad_alloc: Not enough memory.
*/
virtual std::string modename(const std::string& mode) throw(std::bad_alloc) = 0;
/**
* Is this dumper busy dumping?
*
* Return: True if busy, false if not.
*/
virtual bool busy() = 0;
/**
* Start dump.
*
* parameter mode: The mode to dump using.
* parameter targetname: The target filename or prefix.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Can't start dump.
*/
virtual void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc,
std::runtime_error) = 0;
/**
* End current dump.
*/
virtual void end() throw() = 0;
private:
std::string d_id;
};
#endif

View file

@ -429,6 +429,16 @@ public:
* Call on_status_update() in all objects.
*/
static void do_status_update() throw();
/**
* Notify that some dumper has attached, deattached, popped into existence or disappeared.
*
* Default implementation does nothing.
*/
virtual void on_dumper_update();
/**
* Call on_dumper_update on on all objects.
*/
static void do_dumper_update() throw();
protected:
/**
* Call to indicate this target is interested in sound sample data.

View file

@ -268,4 +268,13 @@ private:
*/
std::string format_address(void* addr);
/**
* Get state of running global ctors flag.
*/
bool in_global_ctors();
/**
* Clear the global ctors flag.
*/
void reached_main();
#endif

View file

@ -0,0 +1,23 @@
#ifndef _plat_wxwidgets__menu_dump__hpp__included__
#define _plat_wxwidgets__menu_dump__hpp__included__
#include <wx/string.h>
#include <wx/wx.h>
class dumper_menu_monitor;
class dumper_menu : public wxMenu
{
public:
dumper_menu(wxWindow* win, int wxid_low, int wxid_high);
~dumper_menu();
void on_select(wxCommandEvent& e);
void update();
private:
dumper_menu_monitor* monitor;
wxWindow* pwin;
int wxid_range_low;
int wxid_range_high;
};
#endif

View file

@ -49,6 +49,7 @@ private:
std::stack<wxMenu*> upper;
void* ahmenu;
void* sounddev;
void* dmenu;
};
#endif

36
src/core/advdumper.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "core/advdumper.hpp"
#include "core/dispatch.hpp"
#include "core/globalwrap.hpp"
#include <map>
#include <string>
namespace
{
globalwrap<std::map<std::string, adv_dumper*>> dumpers;
}
const std::string& adv_dumper::id() throw()
{
return d_id;
}
adv_dumper::~adv_dumper()
{
dumpers().erase(d_id);
information_dispatch::do_dumper_update();
}
std::set<adv_dumper*> adv_dumper::get_dumper_set() throw(std::bad_alloc)
{
std::set<adv_dumper*> d;
for(auto i : dumpers())
d.insert(i.second);
return d;
}
adv_dumper::adv_dumper(const std::string& id) throw(std::bad_alloc)
{
d_id = id;
dumpers()[d_id] = this;
}

View file

@ -1,6 +1,7 @@
#include "cscd.hpp"
#include "sox.hpp"
#include "core/advdumper.hpp"
#include "core/command.hpp"
#include "core/dispatch.hpp"
#include "core/lua.hpp"
@ -153,11 +154,8 @@ namespace
avi_avsnoop* vid_dumper;
function_ptr_command<const std::string&> avi_dump("dump-avi", "Start AVI capture",
"Syntax: dump-avi <prefix>\nStart AVI capture to <prefix>.\n",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
tokensplitter t(args);
std::string prefix = t.tail();
void startdump(const std::string& prefix)
{
if(prefix == "")
throw std::runtime_error("Expected prefix");
if(vid_dumper)
@ -178,11 +176,11 @@ namespace
throw std::runtime_error(x.str());
}
messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl;
});
information_dispatch::do_dumper_update();
}
function_ptr_command<> end_avi("end-avi", "End AVI capture",
"Syntax: end-avi\nEnd a AVI capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
void enddump()
{
if(!vid_dumper)
throw std::runtime_error("No AVI(CSCD) video dump in progress");
try {
@ -195,5 +193,67 @@ namespace
}
delete vid_dumper;
vid_dumper = NULL;
information_dispatch::do_dumper_update();
}
function_ptr_command<const std::string&> avi_dump("dump-avi", "Start AVI capture",
"Syntax: dump-avi <prefix>\nStart AVI capture to <prefix>.\n",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
tokensplitter t(args);
std::string prefix = t.tail();
startdump(prefix);
});
function_ptr_command<> end_avi("end-avi", "End AVI capture",
"Syntax: end-avi\nEnd a AVI capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
enddump();
});
class adv_avi_dumper : public adv_dumper
{
public:
adv_avi_dumper() : adv_dumper("INTERNAL-AVI-CSCD") {information_dispatch::do_dumper_update(); }
~adv_avi_dumper() throw();
std::set<std::string> list_submodes() throw(std::bad_alloc)
{
std::set<std::string> x;
return x;
}
bool wants_prefix(const std::string& mode) throw()
{
return true;
}
std::string name() throw(std::bad_alloc)
{
return "AVI (internal CSCD)";
}
std::string modename(const std::string& mode) throw(std::bad_alloc)
{
return "";
}
bool busy()
{
return (vid_dumper != NULL);
}
void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc,
std::runtime_error)
{
startdump(targetname);
}
void end() throw()
{
enddump();
}
} adv;
adv_avi_dumper::~adv_avi_dumper() throw()
{
}
}

View file

@ -546,3 +546,19 @@ void information_dispatch::enable_send_sound() throw(std::bad_alloc)
{
dispatch_audio().push_back(this);
}
void information_dispatch::on_dumper_update()
{
//Do nothing.
}
void information_dispatch::do_dumper_update() throw()
{
if(in_global_ctors())
return;
for(auto& i : dispatch()) {
START_EH_BLOCK
i->on_dumper_update();
END_EH_BLOCK(i, "on_dumper_update");
}
}

View file

@ -1,5 +1,6 @@
#include "jmd.hpp"
#include "core/advdumper.hpp"
#include "core/command.hpp"
#include "core/dispatch.hpp"
#include "core/lua.hpp"
@ -134,11 +135,8 @@ namespace
jmd_avsnoop* vid_dumper;
function_ptr_command<const std::string&> jmd_dump("dump-jmd", "Start JMD capture",
"Syntax: dump-jmd <file>\nStart JMD capture to <file>.\n",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
tokensplitter t(args);
std::string prefix = t.tail();
void startdump(std::string prefix)
{
if(prefix == "")
throw std::runtime_error("Expected filename");
if(vid_dumper)
@ -154,11 +152,11 @@ namespace
throw std::runtime_error(x.str());
}
messages << "Dumping to " << prefix << " at level " << level2 << std::endl;
});
information_dispatch::do_dumper_update();
}
function_ptr_command<> end_avi("end-jmd", "End JMD capture",
"Syntax: end-jmd\nEnd a JMD capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
void enddump()
{
if(!vid_dumper)
throw std::runtime_error("No JMD video dump in progress");
try {
@ -171,5 +169,67 @@ namespace
}
delete vid_dumper;
vid_dumper = NULL;
information_dispatch::do_dumper_update();
}
function_ptr_command<const std::string&> jmd_dump("dump-jmd", "Start JMD capture",
"Syntax: dump-jmd <file>\nStart JMD capture to <file>.\n",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
tokensplitter t(args);
std::string prefix = t.tail();
startdump(prefix);
});
function_ptr_command<> end_avi("end-jmd", "End JMD capture",
"Syntax: end-jmd\nEnd a JMD capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
enddump();
});
class adv_jmd_dumper : public adv_dumper
{
public:
adv_jmd_dumper() : adv_dumper("INTERNAL-JMD") {information_dispatch::do_dumper_update(); }
~adv_jmd_dumper() throw();
std::set<std::string> list_submodes() throw(std::bad_alloc)
{
std::set<std::string> x;
return x;
}
bool wants_prefix(const std::string& mode) throw()
{
return false;
}
std::string name() throw(std::bad_alloc)
{
return "JMD";
}
std::string modename(const std::string& mode) throw(std::bad_alloc)
{
return "";
}
bool busy()
{
return (vid_dumper != NULL);
}
void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc,
std::runtime_error)
{
startdump(targetname);
}
void end() throw()
{
enddump();
}
} adv;
adv_jmd_dumper::~adv_jmd_dumper() throw()
{
}
}

View file

@ -21,6 +21,7 @@ namespace
{
std::string rseed;
uint64_t rcounter = 0;
bool reached_main_flag;
std::string get_random_hexstring_64(size_t index)
{
@ -306,4 +307,14 @@ std::string format_address(void* addr)
return y.str();
}
bool in_global_ctors()
{
return !reached_main_flag;
}
void reached_main()
{
reached_main_flag = true;
}
std::string bsnes_core_version;

View file

@ -1,4 +1,5 @@
#include "sdmp.hpp"
#include "core/advdumper.hpp"
#include "core/command.hpp"
#include "core/dispatch.hpp"
#include "core/lua.hpp"
@ -56,34 +57,18 @@ namespace
sdmp_avsnoop* vid_dumper;
function_ptr_command<const std::string&> jmd_dump("dump-sdmp", "Start sdmp capture",
"Syntax: dump-sdmp <prefix>\nStart SDMP capture to <prefix>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
throw std::runtime_error("Expected prefix");
if(vid_dumper)
throw std::runtime_error("SDMP Dump already in progress");
try {
vid_dumper = new sdmp_avsnoop(prefix, false);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
std::ostringstream x;
x << "Error starting SDMP dump: " << e.what();
throw std::runtime_error(x.str());
}
messages << "Dumping SDMP to " << prefix << std::endl;
});
function_ptr_command<const std::string&> jmd_dumpss("dump-sdmpss", "Start SS sdmp capture",
"Syntax: dump-sdmpss <file>\nStart SS SDMP capture to <file>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
void startdump(bool ss, const std::string& prefix)
{
if(prefix == "") {
if(ss)
throw std::runtime_error("Expected filename");
else
throw std::runtime_error("Expected prefix");
}
if(vid_dumper)
throw std::runtime_error("SDMP Dump already in progress");
try {
vid_dumper = new sdmp_avsnoop(prefix, true);
vid_dumper = new sdmp_avsnoop(prefix, ss);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
@ -91,12 +76,15 @@ namespace
x << "Error starting SDMP dump: " << e.what();
throw std::runtime_error(x.str());
}
if(ss)
messages << "Dumping SDMP (SS) to " << prefix << std::endl;
else
messages << "Dumping SDMP to " << prefix << std::endl;
});
information_dispatch::do_dumper_update();
}
function_ptr_command<> end_avi("end-sdmp", "End SDMP capture",
"Syntax: end-sdmp\nEnd a SDMP capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
void enddump()
{
if(!vid_dumper)
throw std::runtime_error("No SDMP video dump in progress");
try {
@ -109,5 +97,73 @@ namespace
}
delete vid_dumper;
vid_dumper = NULL;
information_dispatch::do_dumper_update();
}
function_ptr_command<const std::string&> sdmp_dump("dump-sdmp", "Start sdmp capture",
"Syntax: dump-sdmp <prefix>\nStart SDMP capture to <prefix>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
startdump(false, prefix);
});
function_ptr_command<const std::string&> sdmp_dumpss("dump-sdmpss", "Start SS sdmp capture",
"Syntax: dump-sdmpss <file>\nStart SS SDMP capture to <file>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
startdump(true, prefix);
});
function_ptr_command<> end_avi("end-sdmp", "End SDMP capture",
"Syntax: end-sdmp\nEnd a SDMP capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
enddump();
});
class adv_sdmp_dumper : public adv_dumper
{
public:
adv_sdmp_dumper() : adv_dumper("INTERNAL-SDMP") { information_dispatch::do_dumper_update(); }
~adv_sdmp_dumper() throw();
std::set<std::string> list_submodes() throw(std::bad_alloc)
{
std::set<std::string> x;
x.insert("ss");
x.insert("ms");
return x;
}
bool wants_prefix(const std::string& mode) throw()
{
return (mode != "ss");
}
std::string name() throw(std::bad_alloc)
{
return "SDMP";
}
std::string modename(const std::string& mode) throw(std::bad_alloc)
{
return (mode == "ss" ? "Single-Segment" : "Multi-Segment");
}
bool busy()
{
return (vid_dumper != NULL);
}
void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc,
std::runtime_error)
{
startdump((mode == "ss"), targetname);
}
void end() throw()
{
enddump();
}
} adv;
adv_sdmp_dumper::~adv_sdmp_dumper() throw()
{
}
}

View file

@ -141,6 +141,7 @@ namespace
int main(int argc, char** argv)
{
reached_main();
std::vector<std::string> cmdline;
for(int i = 1; i < argc; i++)
cmdline.push_back(argv[i]);

View file

@ -0,0 +1,162 @@
#include "core/advdumper.hpp"
#include "core/dispatch.hpp"
#include "plat-wxwidgets/menu_dump.hpp"
#include "plat-wxwidgets/platform.hpp"
class dumper_menu_monitor : public information_dispatch
{
public:
dumper_menu_monitor(dumper_menu* dmenu)
: information_dispatch("wxwidgets-dumpmenu")
{
linked = dmenu;
}
~dumper_menu_monitor()
{
}
void on_dumper_update()
{
runuifun([linked]() { if(linked) linked->update(); });
}
private:
dumper_menu* linked;
};
namespace
{
struct dumper_info
{
adv_dumper* instance;
std::string name;
bool active;
std::map<std::string, std::string> modes;
};
std::map<std::string, dumper_info> existing_dumpers;
std::map<std::string, dumper_info> new_dumpers;
struct dumper_menu_struct
{
int end_wxid;
wxMenuItem* end_item;
std::map<int, std::string> start_wxids;
std::map<int, wxMenuItem*> start_items;
wxMenuItem* sep;
};
std::map<std::string, dumper_menu_struct> menustructure;
std::string last_processed;
bool first;
void update_dumperinfo(adv_dumper* d)
{
struct dumper_info inf;
inf.instance = d;
inf.name = d->name();
std::set<std::string> mset = d->list_submodes();
for(auto i : mset)
inf.modes[i] = d->modename(i);
inf.active = d->busy();
new_dumpers[d->id()] = inf;
}
void fetch_dumperinfo()
{
runemufn([]() {
std::set<adv_dumper*> dset = adv_dumper::get_dumper_set();
for(auto i : dset)
update_dumperinfo(i);
});
}
}
dumper_menu::dumper_menu(wxWindow* win, int wxid_low, int wxid_high)
{
pwin = win;
win->Connect(wxid_low, wxid_high, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(dumper_menu::on_select), NULL, this);
wxid_range_low = wxid_low;
wxid_range_high = wxid_high;
monitor = new dumper_menu_monitor(this);
update();
}
dumper_menu::~dumper_menu()
{
delete monitor;
}
void dumper_menu::on_select(wxCommandEvent& e)
{
int id = e.GetId();
if(id < wxid_range_low || id > wxid_range_high)
return;
for(auto i : menustructure) {
adv_dumper* t = existing_dumpers[i.first].instance;
if(i.second.end_wxid == id) {
//Execute end of dump operation.
runemufn([t]() { t->end(); });
return;
}
if(i.second.start_wxids.count(id)) {
//Execute start of dump operation.
std::string mode = i.second.start_wxids[id];
bool prefixed = t->wants_prefix(mode);
std::string prefix;
wxFileDialog* d = new wxFileDialog(pwin, towxstring(prefixed ? std::string("Choose prefix") :
std::string("Choose file")), wxT("."));
if(d->ShowModal() == wxID_OK)
prefix = tostdstring(d->GetPath());
d->Destroy();
if(prefix == "")
return;
runemufn([t, mode, prefix]() { t->start(mode, prefix); });
return;
}
}
}
void dumper_menu::update()
{
fetch_dumperinfo();
//Destroy all old entries.
for(auto i : menustructure) {
struct dumper_menu_struct& m = i.second;
if(m.end_item)
Remove(m.end_item);
for(auto mi : m.start_items)
Remove(mi.second);
if(m.sep)
Remove(m.sep);
}
//And create new ones.
int id = wxid_range_low;
first = true;
menustructure.clear();
for(auto i : new_dumpers) {
if(!first)
menustructure[last_processed].sep = AppendSeparator();
last_processed = i.first;
first = false;
menustructure[i.first].end_item = NULL;
menustructure[i.first].end_wxid = wxID_ANY;
if(!i.second.active) {
if(i.second.modes.empty()) {
menustructure[i.first].start_items[id] = Append(id, towxstring("Dump " +
i.second.name));
menustructure[i.first].start_wxids[id++] = "";
}
for(auto j : i.second.modes) {
menustructure[i.first].start_items[id] = Append(id, towxstring("Dump " +
i.second.name + " (" + j.second + ")"));
menustructure[i.first].start_wxids[id++] = j.second;
}
} else {
menustructure[i.first].end_item = Append(id, towxstring("End " + i.second.name));
menustructure[i.first].end_wxid = id++;
}
}
existing_dumpers = new_dumpers;
}

View file

@ -214,6 +214,7 @@ IMPLEMENT_APP(lsnes_app)
bool lsnes_app::OnInit()
{
reached_main();
set_random_seed();
bring_app_foreground();

View file

@ -17,6 +17,7 @@
#include <vector>
#include <string>
#include "plat-wxwidgets/menu_dump.hpp"
#include "plat-wxwidgets/platform.hpp"
#include "plat-wxwidgets/window_mainwindow.hpp"
@ -64,6 +65,8 @@ enum
wxID_EDIT_MEMORYWATCH,
wxID_SAVE_MEMORYWATCH,
wxID_LOAD_MEMORYWATCH,
wxID_DUMP_FIRST,
wxID_DUMP_LAST = wxID_DUMP_FIRST + 1023
};
@ -777,7 +780,7 @@ wxwin_mainwindow::wxwin_mainwindow()
menu_entry(wxID_EXIT, wxT("&Quit"));
menu_separator();
menu_entry(wxID_ABOUT, wxT("About"));
//File menu: (ACFOS)DELMNPRTV
//File menu: (ACFOS)DELMNPRTUV
menu_start(wxT("&File"));
menu_entry_check(wxID_READONLY_MODE, wxT("Reado&nly mode"));
menu_check(wxID_READONLY_MODE, is_readonly_mode());
@ -793,6 +796,8 @@ wxwin_mainwindow::wxwin_mainwindow()
menu_separator();
menu_entry(wxID_SAVE_SCREENSHOT, wxT("Save sc&reenshot"));
menu_separator();
menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
wxID_DUMP_FIRST, wxID_DUMP_LAST)));
//Autohold menu: (ACFOS)
menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu*>(ahmenu = new autohold_menu(this)));
blistener->set_autohold_menu(reinterpret_cast<autohold_menu*>(ahmenu));

View file

@ -151,6 +151,7 @@ class my_interfaced : public SNES::Interface
int main(int argc, char** argv)
{
reached_main();
std::vector<std::string> cmdline;
for(int i = 1; i < argc; i++)
cmdline.push_back(argv[i]);

View file

@ -52,6 +52,7 @@ std::string escape_string(std::string x)
int main(int argc, char** argv)
{
reached_main();
if(argc != 2) {
std::cerr << "Syntax: " << argv[0] << " <moviefile>" << std::endl;
return 1;

View file

@ -795,8 +795,11 @@ void syntax()
std::cerr << "-8\tDump using 8 bits instead of 16 bits." << std::endl;
}
void reached_main();
int main(int argc, char** argv)
{
reached_main();
if(argc < 4) {
syntax();
return 1;