Wxwidgets: Plugin manager

This commit is contained in:
Ilari Liusvaara 2013-12-29 00:03:57 +02:00
parent f4eb4e3073
commit 0d39cfc692
11 changed files with 387 additions and 7 deletions

View file

@ -4,7 +4,7 @@
#include "library/loadlib.hpp"
void handle_post_loadlibrary();
void autoload_libraries();
void autoload_libraries(void(*on_error)(const std::string& err) = NULL);
void with_loaded_library(const loadlib::module& l);
#endif

View file

@ -8,6 +8,7 @@ std::set<std::string> enumerate_directory(const std::string& dir, const std::str
std::string get_absolute_path(const std::string& relative);
uintmax_t file_get_size(const std::string& path);
time_t file_get_mtime(const std::string& path);
bool file_exists(const std::string& filename);
bool file_is_regular(const std::string& filename);
bool file_is_directory(const std::string& filename);
bool ensure_directory_exists(const std::string& path);

View file

@ -63,6 +63,7 @@ void wxeditor_tasinput_display(wxWindow* parent);
void wxeditor_macro_display(wxWindow* parent);
void wxeditor_hexedit_display(wxWindow* parent);
void wxeditor_multitrack_display(wxWindow* parent);
bool wxeditor_plugin_manager_display(wxWindow* parent);
//Auxillary windows.
void wxwindow_memorysearch_display();

View file

@ -227,6 +227,15 @@ Load <file> as movie or savestate file on startup.
Instead of starting the emulator, only display the settings.
\end_layout
\begin_layout Subsubsection
--pluginmanager
\end_layout
\begin_layout Standard
Instead of starting the emulator, display the plugin manager (useful to
disable some plugin that causes emulator to crash on startup)
\end_layout
\begin_layout Subsubsection
--lua=<file>
\end_layout

View file

@ -83,7 +83,13 @@ Load <file> as movie or savestate file on startup.
Instead of starting the emulator, only display the settings.
4.1.5 --lua=<file>
4.1.5 --pluginmanager
Instead of starting the emulator, display the plugin manager
(useful to disable some plugin that causes emulator to crash on
startup)
4.1.6 --lua=<file>
Run this Lua file on startup

View file

@ -29,12 +29,28 @@ void with_loaded_library(const loadlib::module& l)
}
}
void autoload_libraries()
void autoload_libraries(void(*on_error)(const std::string& err))
{
try {
std::string extension = loadlib::library::extension();
auto libs = enumerate_directory(get_config_path() + "/autoload", ".*");
for(auto i : libs)
with_loaded_library(*new loadlib::module(loadlib::library(i)));
for(auto i : libs) {
if(i.length() < extension.length() + 1)
continue;
if(i[i.length() - extension.length() - 1] != '.')
continue;
std::string tmp = i;
if(tmp.substr(i.length() - extension.length()) != extension)
continue;
try {
with_loaded_library(*new loadlib::module(loadlib::library(i)));
} catch(std::exception& e) {
std::string x = "Can't load '" + i + "': " + e.what();
if(on_error)
on_error(x);
messages << x << std::endl;
}
}
handle_post_loadlibrary();
} catch(std::exception& e) {
messages << e.what() << std::endl;

View file

@ -41,6 +41,12 @@ time_t file_get_mtime(const std::string& path)
return boost_fs::last_write_time(boost_fs::path(path));
}
bool file_exists(const std::string& filename)
{
boost::system::error_code ec;
return boost_fs::exists(boost_fs::path(filename), ec);
}
bool file_is_regular(const std::string& filename)
{
boost::system::error_code ec;

View file

@ -23,7 +23,7 @@ namespace zip
int rename_overwrite(const char* oldname, const char* newname)
{
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
return MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING);
return MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING) ? 0 : -1;
#else
return rename(oldname, newname);
#endif

View file

@ -0,0 +1,326 @@
#include <wx/wx.h>
#include <wx/event.h>
#include <wx/control.h>
#include <wx/combobox.h>
#include <wx/radiobut.h>
#include "platform/wxwidgets/platform.hpp"
#include "platform/wxwidgets/loadsave.hpp"
#include "core/misc.hpp"
#include "core/window.hpp"
#include "library/directory.hpp"
#include "library/loadlib.hpp"
#include "library/string.hpp"
#include "library/zip.hpp"
#include <iostream>
#include <cerrno>
#include <cstring>
#if defined(_WIN32) || defined(_WIN64)
#else
#include <sys/stat.h>
#endif
namespace
{
std::string get_name(std::string path)
{
#if defined(_WIN32) || defined(_WIN64)
const char* sep = "\\/";
#else
const char* sep = "/";
#endif
size_t p = path.find_last_of(sep);
std::string name;
if(p == std::string::npos)
name = path;
else
name = path.substr(p + 1);
return name;
}
std::string strip_extension(std::string tmp, std::string ext)
{
regex_results r = regex("(.*)\\." + ext + "(|\\.disabled)", tmp);
if(!r) return tmp;
return r[1];
}
}
class wxeditor_plugins : public wxDialog
{
public:
wxeditor_plugins(wxWindow* parent);
void on_selection_change(wxCommandEvent& e);
void on_add(wxCommandEvent& e);
void on_rename(wxCommandEvent& e);
void on_enable(wxCommandEvent& e);
void on_delete(wxCommandEvent& e);
void on_start(wxCommandEvent& e);
void on_close(wxCommandEvent& e);
private:
void reload_plugins();
wxListBox* plugins;
wxButton* addbutton;
wxButton* renamebutton;
wxButton* enablebutton;
wxButton* deletebutton;
wxButton* startbutton;
wxButton* closebutton;
std::vector<std::pair<std::string, bool>> pluginstbl;
std::string extension;
std::string pathpfx;
};
wxeditor_plugins::wxeditor_plugins(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Plugin manager"), wxDefaultPosition, wxSize(-1, -1))
{
Center();
wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
SetSizer(top_s);
pathpfx = get_config_path() + "/autoload";
extension = loadlib::library::extension();
top_s->Add(plugins = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(400, 300)), 1, wxGROW);
plugins->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
wxCommandEventHandler(wxeditor_plugins::on_selection_change), NULL, this);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->Add(addbutton = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
pbutton_s->Add(renamebutton = new wxButton(this, wxID_ANY, wxT("Rename")), 0, wxGROW);
pbutton_s->Add(enablebutton = new wxButton(this, wxID_ANY, wxT("Enable")), 0, wxGROW);
pbutton_s->Add(deletebutton = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
pbutton_s->AddStretchSpacer();
if(!parent)
pbutton_s->Add(startbutton = new wxButton(this, wxID_ANY, wxT("Start")), 0, wxGROW);
else
startbutton = NULL;
if(!parent)
pbutton_s->Add(closebutton = new wxButton(this, wxID_EXIT, wxT("Quit")), 0, wxGROW);
else
pbutton_s->Add(closebutton = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
addbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_add), NULL,
this);
renamebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_rename), NULL,
this);
enablebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_enable), NULL,
this);
deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_delete), NULL,
this);
if(startbutton)
startbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_start),
NULL, this);
closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_plugins::on_close), NULL,
this);
top_s->Add(pbutton_s, 0, wxGROW);
reload_plugins();
wxCommandEvent e;
on_selection_change(e);
Fit();
}
void wxeditor_plugins::reload_plugins()
{
int sel = plugins->GetSelection();
std::string name;
if(sel == wxNOT_FOUND || sel >= pluginstbl.size())
name = "";
else
name = pluginstbl[sel].first;
auto dir = enumerate_directory(pathpfx, ".*\\." + extension + "(|\\.disabled)");
plugins->Clear();
pluginstbl.clear();
for(auto i : dir) {
regex_results r = regex("(.*)\\." + extension + "(|\\.disabled)", get_name(i));
if(!r) continue;
pluginstbl.push_back(std::make_pair(r[1], r[2] == ""));
std::string r1 = r[1];
plugins->Append(towxstring(r1 + ((r[2] == "") ? "" : " (disabled)")));
}
bool found = false;
for(size_t i = 0; i < pluginstbl.size(); i++) {
if(pluginstbl[i].first == name)
plugins->SetSelection(i);
}
wxCommandEvent e;
on_selection_change(e);
}
void wxeditor_plugins::on_selection_change(wxCommandEvent& e)
{
int sel = plugins->GetSelection();
if(sel == wxNOT_FOUND || sel >= pluginstbl.size()) {
renamebutton->Enable(false);
enablebutton->Enable(false);
deletebutton->Enable(false);
} else {
enablebutton->SetLabel(towxstring(pluginstbl[sel].second ? "Disable" : "Enable"));
renamebutton->Enable(true);
enablebutton->Enable(true);
deletebutton->Enable(true);
}
}
void wxeditor_plugins::on_add(wxCommandEvent& e)
{
try {
std::string file = choose_file_load(this, "Choose plugin to add", ".",
single_type(loadlib::library::extension(), loadlib::library::name()));
std::string name = strip_extension(get_name(file), extension);
std::string nname = pathpfx + "/" + name + "." + extension;
bool overwrite_ok = false;
bool first = true;
int counter = 2;
while(!overwrite_ok && (file_exists(nname) || file_exists(nname + ".disabled"))) {
if(first) {
wxMessageDialog* d3 = new wxMessageDialog(this,
towxstring("Plugin '" + name + "' already exists.\n\nOverwrite?"),
towxstring("Plugin already exists"),
wxYES_NO | wxCANCEL | wxNO_DEFAULT | wxICON_QUESTION);
int r = d3->ShowModal();
d3->Destroy();
first = false;
if(r == wxID_YES)
break;
if(r == wxID_CANCEL) {
reload_plugins();
return;
}
}
nname = pathpfx + "/" + name + "(" + (stringfmt() << counter++).str() + ")." + extension;
}
std::ifstream in(file, std::ios::binary);
std::ofstream out(nname, std::ios::binary);
if(!out) {
show_message_ok(this, "Error", "Can't write file '" + nname + "'", wxICON_EXCLAMATION);
reload_plugins();
return;
}
if(!in) {
remove(nname.c_str());
show_message_ok(this, "Error", "Can't read file '" + file + "'", wxICON_EXCLAMATION);
reload_plugins();
return;
}
while(true) {
char buf[4096];
size_t r;
r = in.readsome(buf, sizeof(buf));
out.write(buf, r);
if(!r)
break;
}
if(!out) {
remove(nname.c_str());
show_message_ok(this, "Error", "Can't write file '" + nname + "'", wxICON_EXCLAMATION);
reload_plugins();
return;
}
//Set permissions.
#if defined(_WIN32) || defined(_WIN64)
#else
struct stat s;
if(stat(nname.c_str(), &s) < 0)
s.st_mode = 0644;
if(s.st_mode & 0400) s.st_mode |= 0100;
if(s.st_mode & 040) s.st_mode |= 010;
if(s.st_mode & 04) s.st_mode |= 01;
chmod(nname.c_str(), s.st_mode & 0777);
#endif
std::string disname = nname + ".disabled";
remove(disname.c_str());
reload_plugins();
} catch(canceled_exception& e) {
}
}
void wxeditor_plugins::on_rename(wxCommandEvent& e)
{
int sel = plugins->GetSelection();
if(sel == wxNOT_FOUND || sel >= pluginstbl.size())
return;
std::string name = pluginstbl[sel].first;
std::string name2;
try {
name2 = pick_text(this, "Rename plugin to", "Enter new name for plugin", name, false);
} catch(canceled_exception& e) {
return;
}
std::string oname = pathpfx + "/" + name + "." + extension + (pluginstbl[sel].second ? "" : ".disabled");
std::string nname = pathpfx + "/" + name2 + "." + extension + (pluginstbl[sel].second ? "" : ".disabled");
if(oname != nname)
zip::rename_overwrite(oname.c_str(), nname.c_str());
pluginstbl[sel].first = name2;
reload_plugins();
}
void wxeditor_plugins::on_enable(wxCommandEvent& e)
{
int sel = plugins->GetSelection();
if(sel == wxNOT_FOUND || sel >= pluginstbl.size())
return;
std::string ename = pathpfx + "/" + pluginstbl[sel].first + "." + extension;
std::string dname = pathpfx + "/" + pluginstbl[sel].first + "." + extension + ".disabled";
bool ok;
if(pluginstbl[sel].second)
ok = !zip::rename_overwrite(ename.c_str(), dname.c_str());
else
ok = !zip::rename_overwrite(dname.c_str(), ename.c_str());
if(!ok) {
show_message_ok(this, "Error", "Can't enable/disable plugin '" + pluginstbl[sel].first +
"'", wxICON_EXCLAMATION);
reload_plugins();
return;
}
pluginstbl[sel].second = !pluginstbl[sel].second;
reload_plugins();
}
void wxeditor_plugins::on_delete(wxCommandEvent& e)
{
int sel = plugins->GetSelection();
if(sel == wxNOT_FOUND || sel >= pluginstbl.size())
return;
std::string oname = pathpfx + "/" + pluginstbl[sel].first + "." + extension +
(pluginstbl[sel].second ? "" : ".disabled");
if(remove(oname.c_str()) < 0) {
int err = errno;
show_message_ok(this, "Error", "Can't delete plugin '" + pluginstbl[sel].first +
"': " + strerror(err), wxICON_EXCLAMATION);
reload_plugins();
return;
}
reload_plugins();
}
void wxeditor_plugins::on_start(wxCommandEvent& e)
{
EndModal(wxID_OK);
}
void wxeditor_plugins::on_close(wxCommandEvent& e)
{
EndModal(wxID_CANCEL);
}
bool wxeditor_plugin_manager_display(wxWindow* parent)
{
int r;
modal_pause_holder* hld = NULL;
try {
if(parent)
hld = new modal_pause_holder();
wxDialog* editor;
try {
editor = new wxeditor_plugins(parent);
r = editor->ShowModal();
} catch(...) {
}
editor->Destroy();
if(hld) delete hld;
return (r == wxID_OK);
} catch(...) {
if(hld) delete hld;
return false;
}
}

View file

@ -344,6 +344,7 @@ public:
virtual bool OnCmdLineParsed(wxCmdLineParser& parser);
private:
bool settings_mode;
bool pluginmanager_mode;
std::string c_rom;
std::string c_file;
std::vector<std::string> cmdline;
@ -374,6 +375,7 @@ bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser)
regex_results r;
if(i == "--help" || i == "-h") {
std::cout << "--settings: Show the settings dialog" << std::endl;
std::cout << "--pluginmanager: Show the plugin manager" << std::endl;
std::cout << "--rom=<filename>: Load specified ROM on startup" << std::endl;
std::cout << "--load=<filename>: Load specified save/movie on starup" << std::endl;
std::cout << "--lua=<filename>: Load specified Lua script on startup" << std::endl;
@ -384,6 +386,8 @@ bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser)
}
if(i == "--settings")
settings_mode = true;
if(i == "--pluginmanager")
pluginmanager_mode = true;
if(r = regex("--set=([^=]+)=(.+)", i))
c_settings[r[1]] = r[2];
if(r = regex("--lua=(.+)", i))
@ -403,6 +407,10 @@ bool lsnes_app::OnInit()
set_random_seed();
bring_app_foreground();
if(pluginmanager_mode)
if(!wxeditor_plugin_manager_display(NULL))
return false;
ui_services = new ui_services_type();
ui_thread = this_thread_id();
@ -419,7 +427,9 @@ bool lsnes_app::OnInit()
controls.set_ports(ports);
std::string cfgpath = get_config_path();
autoload_libraries();
autoload_libraries([](const std::string& error) {
show_message_ok(NULL, "Error loading plugin", error, wxICON_EXCLAMATION);
});
messages << "Saving per-user data to: " << get_config_path() << std::endl;
messages << "--- Loading configuration --- " << std::endl;
load_configuration();

View file

@ -133,6 +133,7 @@ enum
wxID_DOWNLOAD,
wxID_TRACELOG_FIRST,
wxID_TRACELOG_LAST = wxID_TRACELOG_FIRST + 256,
wxID_PLUGIN_MANAGER,
};
@ -1025,6 +1026,7 @@ wxwin_mainwindow::wxwin_mainwindow()
if(loadlib::library::name() != "") {
menu_separator();
menu_entry(wxID_LOAD_LIBRARY, towxstring(std::string("Load ") + loadlib::library::name()));
menu_entry(wxID_PLUGIN_MANAGER, towxstring("Plugin manager"));
}
menu_separator();
menu_entry(wxID_RELOAD_ROM_IMAGE, wxT("Reload ROM"));
@ -1606,6 +1608,9 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
handle_post_loadlibrary();
break;
}
case wxID_PLUGIN_MANAGER:
wxeditor_plugin_manager_display(this);
return;
case wxID_RELOAD_ROM_IMAGE:
runemufn([]() {
lsnes_cmd.invoke("unpause-emulator");