5 #include "core/audioapi.hpp"
6 #include "core/command.hpp"
7 #include "core/controller.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/framerate.hpp"
10 #include "core/joystickapi.hpp"
11 #include "core/keymapper.hpp"
12 #include "core/loadlib.hpp"
13 #include "lua/lua.hpp"
14 #include "core/advdumper.hpp"
15 #include "core/mainloop.hpp"
16 #include "core/messages.hpp"
17 #include "core/misc.hpp"
18 #include "core/instance.hpp"
19 #include "core/misc.hpp"
20 #include "core/moviefile-common.hpp"
21 #include "core/moviedata.hpp"
22 #include "core/random.hpp"
23 #include "core/rom.hpp"
24 #include "core/settings.hpp"
25 #include "core/window.hpp"
26 #include "interface/romtype.hpp"
27 #include "library/crandom.hpp"
28 #include "library/directory.hpp"
29 #include "library/running-executable.hpp"
30 #include "library/string.hpp"
31 #include "library/threads.hpp"
32 #include "library/utf8.hpp"
33 #include "library/zip.hpp"
35 #include "platform/wxwidgets/settings-common.hpp"
36 #include "platform/wxwidgets/platform.hpp"
37 #include "platform/wxwidgets/window_messages.hpp"
38 #include "platform/wxwidgets/window_status.hpp"
39 #include "platform/wxwidgets/window_mainwindow.hpp"
47 #include <wx/control.h>
48 #include <wx/combobox.h>
49 #include <wx/cmdline.h>
52 #define UISERV_REFRESH_TITLE 9990
53 #define UISERV_RESIZED 9991
54 #define UISERV_UIFUN 9992
55 #define UISERV_EXIT 9994
56 #define UISERV_PANIC 9998
57 #define UISERV_ERROR 9999
59 wxwin_messages
* msg_window
;
60 wxwin_mainwindow
* main_window
;
61 std::string our_rom_name
;
63 bool wxwidgets_exiting
= false;
67 threads::id ui_thread
;
68 volatile bool panic_ack
= false;
69 std::string error_message_text
;
70 volatile bool modal_dialog_confirm
;
71 volatile bool modal_dialog_active
;
72 threads::lock ui_mutex
;
73 threads::cv ui_condition
;
74 bool preboot_env
= true;
75 runuifun_once_ctx screenupdate_once
;
76 runuifun_once_ctx statusupdate_once
;
77 runuifun_once_ctx message_once
;
79 struct uiserv_event
: public wxEvent
81 uiserv_event(int code
)
86 wxEvent
* Clone() const
88 return new uiserv_event(*this);
92 class ui_services_type
: public wxEvtHandler
94 bool ProcessEvent(wxEvent
& event
);
101 runuifun_once_ctx
* ctx
;
104 std::list
<ui_queue_entry
> ui_queue
;
108 std::string msg
= "Panic: Unrecoverable error, can't continue";
109 std::string msg2
= platform::msgbuf
.get_last_message();
111 msg
+= "\n\n" + msg2
;
112 wxMessageBox(towxstring(msg
), _T("Error"), wxICON_ERROR
| wxOK
);
115 bool ui_services_type::ProcessEvent(wxEvent
& event
)
118 int c
= event
.GetId();
119 if(c
== UISERV_PANIC
) {
123 } else if(c
== UISERV_REFRESH_TITLE
) {
125 main_window
->refresh_title();
126 } else if(c
== UISERV_RESIZED
) {
128 main_window
->notify_resized();
129 } else if(c
== UISERV_ERROR
) {
130 std::string text
= error_message_text
;
131 wxMessageBox(towxstring(text
), _T("lsnes: Error"), wxICON_EXCLAMATION
| wxOK
, main_window
);
132 } else if(c
== UISERV_EXIT
) {
134 main_window
->notify_exit();
135 } else if(c
== UISERV_UIFUN
) {
136 std::list
<ui_queue_entry
>::iterator i
;
138 queue_synchronous_fn_warning
= true;
141 threads::alock
h(ui_mutex
);
144 i
= ui_queue
.begin();
148 if(e
.ctx
) e
.ctx
->clear_flag();
152 queue_synchronous_fn_warning
= false;
157 ui_services_type
* ui_services
;
159 void post_ui_event(int code
)
161 uiserv_event
uic(code
);
162 wxPostEvent(ui_services
, uic
);
165 std::string loaded_pdev
;
166 std::string loaded_rdev
;
168 double from_logscale(double v
)
173 void handle_config_line(std::string line
)
176 if(r
= regex("SET[ \t]+([^ \t]+)[ \t]+(.*)", line
)) {
177 lsnes_instance
.setcache
->set(r
[1], r
[2], true);
178 messages
<< "Setting " << r
[1] << " set to " << r
[2] << std::endl
;
179 } else if(r
= regex("ALIAS[ \t]+([^ \t]+)[ \t]+(.*)", line
)) {
180 if(!lsnes_instance
.command
->valid_alias_name(r
[1])) {
181 messages
<< "Illegal alias name " << r
[1] << std::endl
;
184 std::string tmp
= lsnes_instance
.command
->get_alias_for(r
[1]);
185 tmp
= tmp
+ r
[2] + "\n";
186 lsnes_instance
.command
->set_alias_for(r
[1], tmp
);
187 messages
<< r
[1] << " aliased to " << r
[2] << std::endl
;
188 } else if(r
= regex("BIND[ \t]+([^/]*)/([^|]*)\\|([^ \t]+)[ \t]+(.*)", line
)) {
189 std::string tmp
= r
[4];
190 regex_results r2
= regex("(load|load-smart|load-readonly|load-preserve|load-state"
191 "|load-movie|save-state|save-movie)[ \t]+\\$\\{project\\}(.*)\\.lsmv", tmp
);
192 if(r2
) tmp
= r2
[1] + " $SLOT:" + r2
[2];
193 lsnes_instance
.mapper
->bind(r
[1], r
[2], r
[3], tmp
);
194 if(r
[1] != "" || r
[2] != "")
195 messages
<< r
[1] << "/" << r
[2] << " ";
196 messages
<< r
[3] << " bound to '" << tmp
<< "'" << std::endl
;
197 } else if(r
= regex("BUTTON[ \t]+([^ \t]+)[ \t](.*)", line
)) {
198 keyboard::ctrlrkey
* ckey
= lsnes_instance
.mapper
->get_controllerkey(r
[2]);
201 messages
<< r
[1] << " bound (button) to " << r
[2] << std::endl
;
203 lsnes_instance
.buttons
->button_keys
[r
[2]] = r
[1];
204 } else if(r
= regex("PREFER[ \t]+([^ \t]+)[ \t]+(.*)", line
)) {
206 core_selections
[r
[1]] = r
[2];
207 messages
<< "Prefer " << r
[2] << " for " << r
[1] << std::endl
;
209 } else if(r
= regex("AUDIO_PDEV[ \t]+([^ \t].*)", line
)) {
211 } else if(r
= regex("AUDIO_RDEV[ \t]+([^ \t].*)", line
)) {
213 } else if(r
= regex("AUDIO_GVOL[ \t]+([^ \t].*)", line
)) {
214 lsnes_instance
.audio
->music_volume(from_logscale(parse_value
<double>(r
[1])));
215 } else if(r
= regex("AUDIO_RVOL[ \t]+([^ \t].*)", line
)) {
216 lsnes_instance
.audio
->voicer_volume(from_logscale(parse_value
<double>(r
[1])));
217 } else if(r
= regex("AUDIO_PVOL[ \t]+([^ \t].*)", line
)) {
218 lsnes_instance
.audio
->voicep_volume(from_logscale(parse_value
<double>(r
[1])));
219 } else if(r
= regex("VIDEO_ARC[ \t]*", line
)) {
220 arcorrect_enabled
= true;
221 } else if(r
= regex("VIDEO_HFLIP[ \t]*", line
)) {
222 hflip_enabled
= true;
223 } else if(r
= regex("VIDEO_VFLIP[ \t]*", line
)) {
224 vflip_enabled
= true;
225 } else if(r
= regex("VIDEO_ROTATE[ \t]*", line
)) {
226 rotate_enabled
= true;
227 } else if(r
= regex("VIDEO_SFACT[ \t]+([^ \t].*)", line
)) {
228 double val
= parse_value
<double>(r
[1]);
229 if(val
< 0.1 || val
> 10) throw std::runtime_error("Crazy scale factor");
230 video_scale_factor
= val
;
231 } else if(r
= regex("VIDEO_SFLAGS[ \t]+([^ \t].*)", line
)) {
232 scaling_flags
= parse_value
<uint32_t>(r
[1]);
234 (stringfmt() << "Unrecognized directive: " << line
).throwex();
237 void load_configuration()
239 std::string cfg
= get_config_path() + "/lsneswxw.cfg";
240 std::ifstream
cfgfile(cfg
.c_str());
243 while(std::getline(cfgfile
, line
)) {
245 handle_config_line(line
);
246 } catch(std::exception
& e
) {
247 messages
<< "Error processing line " << lineno
<< ": " << e
.what() << std::endl
;
251 platform::set_sound_device_by_description(loaded_pdev
, loaded_rdev
);
252 (*lsnes_instance
.abindmanager
)();
253 lsnes_uri_rewrite
.load(get_config_path() + "/lsnesurirewrite.cfg");
256 double to_logscale(double v
)
263 void save_configuration()
265 std::string cfg
= get_config_path() + "/lsneswxw.cfg";
266 std::string cfgtmp
= cfg
+ ".tmp";
267 std::ofstream
cfgfile(cfgtmp
.c_str());
269 for(auto i
: lsnes_instance
.setcache
->get_all())
270 cfgfile
<< "SET " << i
.first
<< " " << i
.second
<< std::endl
;
272 for(auto i
: lsnes_instance
.command
->get_aliases()) {
273 std::string old_alias_value
= lsnes_instance
.command
->get_alias_for(i
);
274 while(old_alias_value
!= "") {
275 std::string aliasline
;
276 size_t s
= old_alias_value
.find_first_of("\n");
277 if(s
< old_alias_value
.length()) {
278 aliasline
= old_alias_value
.substr(0, s
);
279 old_alias_value
= old_alias_value
.substr(s
+ 1);
281 aliasline
= old_alias_value
;
282 old_alias_value
= "";
284 cfgfile
<< "ALIAS " << i
<< " " << aliasline
<< std::endl
;
288 for(auto i
: lsnes_instance
.mapper
->get_bindings())
289 cfgfile
<< "BIND " << std::string(i
) << " " << lsnes_instance
.mapper
->get(i
) << std::endl
;
291 for(auto i
: lsnes_instance
.mapper
->get_controller_keys()) {
294 while((b
= i
->get_string(idx
++)) != "")
295 cfgfile
<< "BUTTON " << b
<< " " << i
->get_command() << std::endl
;
297 for(auto i
: lsnes_instance
.buttons
->button_keys
)
298 cfgfile
<< "BUTTON " << i
.second
<< " " << i
.first
<< std::endl
;
299 for(auto i
: core_selections
)
301 cfgfile
<< "PREFER " << i
.first
<< " " << i
.second
<< std::endl
;
303 cfgfile
<< "AUDIO_PDEV " << platform::get_sound_device_description(false) << std::endl
;
304 cfgfile
<< "AUDIO_RDEV " << platform::get_sound_device_description(true) << std::endl
;
305 cfgfile
<< "AUDIO_GVOL " << to_logscale(lsnes_instance
.audio
->music_volume()) << std::endl
;
306 cfgfile
<< "AUDIO_RVOL " << to_logscale(lsnes_instance
.audio
->voicer_volume()) << std::endl
;
307 cfgfile
<< "AUDIO_PVOL " << to_logscale(lsnes_instance
.audio
->voicep_volume()) << std::endl
;
308 cfgfile
<< "VIDEO_SFACT " << video_scale_factor
<< std::endl
;
309 cfgfile
<< "VIDEO_SFLAGS " << scaling_flags
<< std::endl
;
310 if(arcorrect_enabled
) cfgfile
<< "VIDEO_ARC" << std::endl
;
311 if(hflip_enabled
) cfgfile
<< "VIDEO_HFLIP" << std::endl
;
312 if(vflip_enabled
) cfgfile
<< "VIDEO_VFLIP" << std::endl
;
313 if(rotate_enabled
) cfgfile
<< "VIDEO_ROTATE" << std::endl
;
315 show_message_ok(NULL
, "Error Saving configuration", "Error saving configuration",
320 directory::rename_overwrite(cfgtmp
.c_str(), cfg
.c_str());
322 std::ofstream
lsave(get_config_path() + "/" + our_rom_name
+ ".ls");
324 lsnes_uri_rewrite
.save(get_config_path() + "/lsnesurirewrite.cfg");
327 void* eloop_helper(int x
)
329 platform::dummy_event_loop();
333 std::string
get_loaded_movie(const std::vector
<std::string
>& cmdline
)
335 for(auto i
: cmdline
)
336 if(!i
.empty() && i
[0] != '-')
342 wxString
towxstring(const std::string
& str
) throw(std::bad_alloc
)
344 return wxString(str
.c_str(), wxConvUTF8
);
347 std::string
tostdstring(const wxString
& str
) throw(std::bad_alloc
)
349 return std::string(str
.mb_str(wxConvUTF8
));
352 wxString
towxstring(const std::u32string
& str
) throw(std::bad_alloc
)
354 return wxString(utf8::to8(str
).c_str(), wxConvUTF8
);
357 std::u32string
tou32string(const wxString
& str
) throw(std::bad_alloc
)
359 return utf8::to32(std::string(str
.mb_str(wxConvUTF8
)));
362 std::string
pick_archive_member(wxWindow
* parent
, const std::string
& filename
) throw(std::bad_alloc
)
365 //Did we pick a .zip file?
368 zip::reader
zr(filename
);
369 std::vector
<wxString
> files
;
371 files
.push_back(towxstring(i
));
372 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(parent
, wxT("Select file within .zip"),
373 wxT("Select member"), files
.size(), &files
[0]);
374 if(d2
->ShowModal() == wxID_CANCEL
) {
378 f
= filename
+ "/" + tostdstring(d2
->GetStringSelection());
387 void signal_program_exit()
389 post_ui_event(UISERV_EXIT
);
392 void signal_resize_needed()
394 post_ui_event(UISERV_RESIZED
);
398 static const wxCmdLineEntryDesc dummy_descriptor_table
[] = {
399 { wxCMD_LINE_PARAM
, NULL
, NULL
, NULL
, wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
|
400 wxCMD_LINE_PARAM_MULTIPLE
},
404 class lsnes_app
: public wxApp
408 virtual bool OnInit();
409 virtual int OnExit();
410 virtual void OnInitCmdLine(wxCmdLineParser
& parser
);
411 virtual bool OnCmdLineParsed(wxCmdLineParser
& parser
);
414 bool pluginmanager_mode
;
417 std::vector
<std::string
> cmdline
;
418 std::map
<std::string
, std::string
> c_settings
;
419 std::vector
<std::string
> c_lua
;
420 std::vector
<std::string
> c_library
;
421 bool exit_immediately
;
422 bool fullscreen_mode
;
424 struct dispatch::target
<> screenupdate
;
425 struct dispatch::target
<> statusupdate
;
426 struct dispatch::target
<> actionupdate
;
429 IMPLEMENT_APP(lsnes_app
)
431 lsnes_app::lsnes_app()
433 settings_mode
= false;
434 pluginmanager_mode
= false;
435 exit_immediately
= false;
436 fullscreen_mode
= false;
437 start_unpaused
= false;
440 void lsnes_app::OnInitCmdLine(wxCmdLineParser
& parser
)
442 parser
.SetDesc(dummy_descriptor_table
);
443 parser
.SetSwitchChars(wxT(""));
446 static bool regex_sanity_check()
448 bool regex_sane
= true;
450 //Simple sanity checks.
451 regex_sane
&= regex_match("foo.*baz", "foobarbaz", REGEX_MATCH_REGEX
);
452 regex_sane
&= regex_match(".*foo.*baz.*", "foobarbaz", REGEX_MATCH_REGEX
);
453 regex_sane
&= regex_match("foo*baz", "FOOBARBAZ", REGEX_MATCH_IWILDCARDS
);
454 regex_sane
&= regex_match("foo.*baz", "FOOBARBAZ", REGEX_MATCH_IREGEX
);
461 bool lsnes_app::OnCmdLineParsed(wxCmdLineParser
& parser
)
463 for(size_t i
= 0; i
< parser
.GetParamCount(); i
++)
464 cmdline
.push_back(tostdstring(parser
.GetParam(i
)));
465 for(auto i
: cmdline
) {
467 if(i
== "--help" || i
== "-h") {
468 std::cout
<< "--settings: Show the settings dialog" << std::endl
;
469 std::cout
<< "--pluginmanager: Show the plugin manager" << std::endl
;
470 std::cout
<< "--fullscreen: Start fullscreen" << std::endl
;
471 std::cout
<< "--unpause: Start unpaused (only if ROM is loaded)" << std::endl
;
472 std::cout
<< "--rom=<filename>: Load specified ROM on startup" << std::endl
;
473 std::cout
<< "--load=<filename>: Load specified save/movie on starup" << std::endl
;
474 std::cout
<< "--lua=<filename>: Load specified Lua script on startup" << std::endl
;
475 std::cout
<< "--library=<filename>: Load specified library on startup" << std::endl
;
476 std::cout
<< "--set=<a>=<b>: Set setting <a> to value <b>" << std::endl
;
477 std::cout
<< "--sanity-check: Perfrom some simple sanity checks" << std::endl
;
478 std::cout
<< "<filename>: Load specified ROM on startup" << std::endl
;
479 exit_immediately
= true;
482 if(i
== "--settings")
483 settings_mode
= true;
485 start_unpaused
= true;
486 if(i
== "--fullscreen")
487 fullscreen_mode
= true;
488 if(i
== "--pluginmanager")
489 pluginmanager_mode
= true;
490 if(r
= regex("--set=([^=]+)=(.+)", i
))
491 c_settings
[r
[1]] = r
[2];
492 if(r
= regex("--lua=(.+)", i
))
493 c_lua
.push_back(r
[1]);
494 if(r
= regex("--library=(.+)", i
))
495 c_library
.push_back(r
[1]);
496 if(i
== "--sanity-check") {
497 if(regex_sanity_check()) {
498 std::cout
<< "Regex library passes basic sanity checks." << std::endl
;
500 std::cout
<< "Regex library FAILS basic sanity checks." << std::endl
;
502 std::cout
<< "Executable: '" << running_executable() << "'" << std::endl
;
503 std::cout
<< "Configuration directory: '" << get_config_path()
505 std::cout
<< "System autoload directory: '" << loadlib_debug_get_system_library_dir()
507 std::cout
<< "User autoload directory: '" << loadlib_debug_get_user_library_dir()
509 exit_immediately
= true;
515 bool lsnes_app::OnInit()
521 screenupdate
.set(lsnes_instance
.dispatch
->screen_update
, []() {
522 runuifun(screenupdate_once
, []() {
524 main_window
->notify_update();
525 wxwindow_memorysearch_update(CORE());
526 wxwindow_tasinput_update(CORE());
529 statusupdate
.set(lsnes_instance
.dispatch
->status_update
, []() {
530 runuifun(statusupdate_once
, []() {
532 main_window
->notify_update_status();
533 wxeditor_movie_update(CORE());
534 wxeditor_hexeditor_update(CORE());
537 actionupdate
.set(lsnes_instance
.dispatch
->action_update
, []() {
538 //This can be called early, so check for main_window existing.
540 main_window
->action_updated();
545 } catch(std::exception
& e
) {
546 show_message_ok(NULL
, "RNG error", "Error initializing system RNG", wxICON_ERROR
);
550 if(!regex_sanity_check()) {
551 wxMessageBox(towxstring("Regex sanity check FAILED.\n\nExpect problems."),
552 _T("lsnes: Error"), wxICON_EXCLAMATION
| wxOK
, NULL
);
557 bring_app_foreground();
559 if(pluginmanager_mode
)
560 if(!wxeditor_plugin_manager_display(NULL
))
563 ui_services
= new ui_services_type();
565 ui_thread
= threads::this_id();
568 messages
<< "lsnes version: lsnes rr" << lsnes_version
<< std::endl
;
570 loaded_rom dummy_rom
;
571 std::map
<std::string
, std::string
> settings
;
572 auto ctrldata
= dummy_rom
.controllerconfig(settings
);
573 portctrl::type_set
& ports
= portctrl::type_set::make(ctrldata
.ports
, ctrldata
.portindex());
575 lsnes_instance
.buttons
->reinit();
576 lsnes_instance
.controls
->set_ports(ports
);
578 std::string cfgpath
= get_config_path();
579 autoload_libraries([](const std::string
& libname
, const std::string
& error
, bool system
) {
580 show_message_ok(NULL
, "Error loading plugin " + libname
, "Error loading '" + libname
+ "'\n\n" +
581 error
, wxICON_EXCLAMATION
);
583 wxeditor_plugin_manager_notify_fail(libname
);
585 messages
<< "Saving per-user data to: " << get_config_path() << std::endl
;
586 messages
<< "--- Loading configuration --- " << std::endl
;
587 load_configuration();
588 messages
<< "--- End running lsnesrc --- " << std::endl
;
591 //We got to boot this up quite a bit to get the joystick driver working.
592 //In practicular, we need joystick thread and emulator thread in pause.
593 threads::thread
* dummy_loop
= new threads::thread(eloop_helper
, 8);
594 display_settings_dialog(NULL
, lsnes_instance
, NULL
);
595 platform::exit_dummy_event_loop();
596 joystick_driver_quit();
598 save_configuration();
601 init_lua(lsnes_instance
);
602 lsnes_instance
.mdumper
->set_output(&messages
.getstream());
604 msg_window
= new wxwin_messages(lsnes_instance
);
607 init_main_callbacks();
609 //Load libraries before trying to load movie, in case there are cores in there.
610 for(auto i
: c_library
) {
612 with_loaded_library(*new loadlib::module(loadlib::library(i
)));
613 } catch(std::exception
& e
) {
614 show_message_ok(NULL
, "Error loading library", std::string("Error loading library '") +
615 i
+ "':\n\n" + e
.what(), wxICON_EXCLAMATION
);
619 const std::string movie_file
= get_loaded_movie(cmdline
);
623 rom
= construct_rom(movie_file
, cmdline
);
624 rom
.load(c_settings
, mov
.movie_rtc_second
, mov
.movie_rtc_subsecond
);
625 } catch(std::exception
& e
) {
626 std::cerr
<< "Can't load ROM: " << e
.what() << std::endl
;
627 show_message_ok(NULL
, "Error loading ROM", std::string("Error loading ROM:\n\n") +
628 e
.what(), wxICON_EXCLAMATION
);
629 quit_lua(lsnes_instance
); //Don't crash.
633 moviefile
* mov
= NULL
;
636 mov
= new moviefile(movie_file
, rom
.get_internal_rom_type());
637 rom
.load(mov
->settings
, mov
->movie_rtc_second
, mov
->movie_rtc_subsecond
);
638 } catch(std::exception
& e
) {
639 std::cerr
<< "Can't load state: " << e
.what() << std::endl
;
640 show_message_ok(NULL
, "Error loading movie", std::string("Error loading movie:\n\n") +
641 e
.what(), wxICON_EXCLAMATION
);
642 quit_lua(lsnes_instance
); //Don't crash.
646 mov
= new moviefile(rom
, c_settings
, DEFAULT_RTC_SECOND
, DEFAULT_RTC_SUBSECOND
);
648 *lsnes_instance
.rom
= rom
;
649 mov
->start_paused
= start_unpaused
? rom
.isnull() : true;
651 lsnes_instance
.lua2
->add_startup_script(i
);
653 boot_emulator(lsnes_instance
, rom
, *mov
, fullscreen_mode
);
657 int lsnes_app::OnExit()
661 //NULL these so no further messages will be sent.
667 save_configuration();
668 quit_lua(lsnes_instance
);
669 lsnes_instance
.mlogic
->release_memory();
671 lsnes_instance
.buttons
->cleanup();
673 deinitialize_wx_mouse(lsnes_instance
);
674 deinitialize_wx_keyboard(lsnes_instance
);
678 void do_save_configuration()
680 save_configuration();
685 struct _graphics_driver drv
= {
686 .init
= []() -> void {
687 initialize_wx_keyboard(lsnes_instance
);
688 initialize_wx_mouse(lsnes_instance
);
690 .quit
= []() -> void {},
691 .notify_message
= []() -> void
693 runuifun(message_once
, []() {
695 msg_window
->notify_update();
698 .error_message
= [](const std::string
& text
) -> void {
699 error_message_text
= text
;
700 post_ui_event(UISERV_ERROR
);
702 .fatal_error
= []() -> void {
703 //Fun: This can be called from any thread!
704 if(ui_thread
== threads::this_id()) {
706 platform::set_modal_pause(true);
709 //Emulation thread panic. Signal the UI thread.
710 post_ui_event(UISERV_PANIC
);
714 .name
= []() -> const char* { return "wxwidgets graphics plugin"; },
715 .request_rom
= [](rom_request
& req
)
717 rom_request
* _req
= &req
;
723 //main_window is NULL, hope this does not crash.
724 main_window
->request_rom(*_req
);
726 _req
->canceled
= true;
730 threads::alock
h(lock
);
731 runuifun([_req
, &lock
, &cv
, &done
]() -> void {
733 main_window
->request_rom(*_req
);
735 _req
->canceled
= true;
737 threads::alock
h(lock
);
745 struct graphics_driver
_drv(drv
);
748 void signal_core_change()
750 post_ui_event(UISERV_REFRESH_TITLE
);
753 void _runuifun_async(runuifun_once_ctx
* ctx
, void (*fn
)(void*), void* arg
)
755 if(ctx
&& !ctx
->set_flag()) return;
756 threads::alock
h(ui_mutex
);
761 ui_queue
.push_back(e
);
762 post_ui_event(UISERV_UIFUN
);
766 canceled_exception::canceled_exception() : std::runtime_error("Dialog canceled") {}
768 std::string
pick_file(wxWindow
* parent
, const std::string
& title
, const std::string
& startdir
)
771 wxString _title
= towxstring(title
);
772 wxString _startdir
= towxstring(startdir
);
773 std::string filespec
;
774 filespec
= "All files|*";
775 wxFileDialog
* d
= new wxFileDialog(parent
, _title
, _startdir
, wxT(""), towxstring(filespec
), wxFD_OPEN
);
776 if(d
->ShowModal() == wxID_CANCEL
)
777 throw canceled_exception();
778 std::string filename
= tostdstring(d
->GetPath());
781 throw canceled_exception();
785 std::string
pick_file_member(wxWindow
* parent
, const std::string
& title
, const std::string
& startdir
)
788 std::string filename
= pick_file(parent
, title
, startdir
);
789 //Did we pick a .zip file?
790 if(!regex_match(".*\\.[zZ][iI][pP]", filename
))
791 return filename
; //Not a ZIP.
793 zip::reader
zr(filename
);
794 std::vector
<std::string
> files
;
797 filename
= filename
+ "/" + pick_among(parent
, "Select member", "Select file within .zip", files
);
798 } catch(canceled_exception
& e
) {
799 //Throw these forward.
807 unsigned pick_among_index(wxWindow
* parent
, const std::string
& title
, const std::string
& prompt
,
808 const std::vector
<std::string
>& choices
, unsigned defaultchoice
)
811 std::vector
<wxString
> _choices
;
812 for(auto i
: choices
)
813 _choices
.push_back(towxstring(i
));
814 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(parent
, towxstring(prompt
), towxstring(title
),
815 _choices
.size(), &_choices
[0]);
816 d2
->SetSelection(defaultchoice
);
817 if(d2
->ShowModal() == wxID_CANCEL
) {
819 throw canceled_exception();
821 unsigned idx
= d2
->GetSelection();
826 std::string
pick_among(wxWindow
* parent
, const std::string
& title
, const std::string
& prompt
,
827 const std::vector
<std::string
>& choices
, unsigned defaultchoice
)
829 unsigned idx
= pick_among_index(parent
, title
, prompt
, choices
, defaultchoice
);
830 if(idx
< choices
.size())
832 throw canceled_exception();
835 std::string
pick_text(wxWindow
* parent
, const std::string
& title
, const std::string
& prompt
, const std::string
& dflt
,
839 wxTextEntryDialog
* d2
= new wxTextEntryDialog(parent
, towxstring(prompt
), towxstring(title
), towxstring(dflt
),
840 wxOK
| wxCANCEL
| wxCENTRE
| (multiline
? wxTE_MULTILINE
: 0));
841 if(d2
->ShowModal() == wxID_CANCEL
) {
843 throw canceled_exception();
845 std::string text
= tostdstring(d2
->GetValue());
850 void show_message_ok(wxWindow
* parent
, const std::string
& title
, const std::string
& text
, int icon
)
853 wxMessageDialog
* d3
= new wxMessageDialog(parent
, towxstring(text
), towxstring(title
), wxOK
| icon
);
858 bool run_show_error(wxWindow
* parent
, const std::string
& title
, const std::string
& text
, std::function
<void()> fn
)
863 } catch(std::exception
& e
) {
864 std::string err
= e
.what();
865 std::string _title
= title
;
866 std::string _text
= (text
== "") ? err
: (text
+ ": " + err
);
867 runuifun([parent
, _title
, _text
]() {
868 show_message_ok(parent
, _title
, _text
, wxICON_EXCLAMATION
);
874 void show_exception(wxWindow
* parent
, const std::string
& title
, const std::string
& text
, std::exception
& e
)
877 std::string err
= e
.what();
878 std::string _title
= title
;
879 std::string _text
= (text
== "") ? err
: (text
+ ": " + err
);
880 show_message_ok(parent
, _title
, _text
, wxICON_EXCLAMATION
);
883 void show_exception_any(wxWindow
* parent
, const std::string
& title
, const std::string
& text
, std::exception
& e
)
885 std::string err
= e
.what();
886 std::string _title
= title
;
887 std::string _text
= (text
== "") ? err
: (text
+ ": " + err
);
888 runuifun([parent
, _title
, _text
]() {
889 show_message_ok(parent
, _title
, _text
, wxICON_EXCLAMATION
);
893 void _check_ui_thread(const char* file
, int line
)
895 if(ui_thread
== threads::this_id())
897 std::cerr
<< "UI routine running in wrong thread at " << file
<< ":" << line
<< std::endl
;