1 //Gaah... wx/wx.h (contains something that breaks if included after snes/snes.hpp from bsnes v085.
5 #include "core/emucore.hpp"
7 #include "core/command.hpp"
8 #include "core/controller.hpp"
9 #include "core/dispatch.hpp"
10 #include "core/framerate.hpp"
11 #include "lua/lua.hpp"
12 #include "core/mainloop.hpp"
13 #include "core/misc.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/rom.hpp"
16 #include "core/rrdata.hpp"
17 #include "core/settings.hpp"
18 #include "core/window.hpp"
19 #include "library/string.hpp"
20 #include "library/zip.hpp"
22 #include "platform/wxwidgets/platform.hpp"
23 #include "platform/wxwidgets/window_messages.hpp"
24 #include "platform/wxwidgets/window_status.hpp"
25 #include "platform/wxwidgets/window_mainwindow.hpp"
28 #include <boost/lexical_cast.hpp>
32 #include <wx/control.h>
33 #include <wx/combobox.h>
34 #include <wx/cmdline.h>
36 #define UISERV_RESIZED 9991
37 #define UISERV_UIFUN 9992
38 //#define UISERV_UI_IRQ 9993 Not in use anymore, can be recycled.
39 #define UISERV_EXIT 9994
40 #define UISERV_UPDATE_STATUS 9995
41 #define UISERV_UPDATE_MESSAGES 9996
42 #define UISERV_UPDATE_SCREEN 9997
43 #define UISERV_PANIC 9998
44 #define UISERV_MODAL 9999
46 wxwin_messages
* msg_window
;
47 wxwin_mainwindow
* main_window
;
48 std::string our_rom_name
;
50 bool dummy_interface
= false;
51 bool wxwidgets_exiting
= false;
56 volatile bool panic_ack
= false;
57 std::string modal_dialog_text
;
58 volatile bool modal_dialog_confirm
;
59 volatile bool modal_dialog_active
;
61 condition
* ui_condition
;
62 thread
* joystick_thread_handle
;
64 void* joystick_thread(void* _args
)
66 joystick_plugin::thread_fn();
69 struct uiserv_event
: public wxEvent
71 uiserv_event(int code
)
76 wxEvent
* Clone() const
78 return new uiserv_event(*this);
82 class ui_services_type
: public wxEvtHandler
84 bool ProcessEvent(wxEvent
& event
);
93 std::list
<ui_queue_entry
> ui_queue
;
95 bool ui_services_type::ProcessEvent(wxEvent
& event
)
97 int c
= event
.GetId();
98 if(c
== UISERV_PANIC
) {
100 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"), wxICON_ERROR
|
103 } else if(c
== UISERV_RESIZED
) {
105 main_window
->notify_resized();
106 } else if(c
== UISERV_MODAL
) {
110 mutex::holder
h(*ui_mutex
);
111 text
= modal_dialog_text
;
112 confirm
= modal_dialog_confirm
;
115 int ans
= wxMessageBox(towxstring(text
), _T("Question"), wxICON_QUESTION
| wxOK
|
116 wxCANCEL
, main_window
);
117 confirm
= (ans
== wxOK
);
119 wxMessageBox(towxstring(text
), _T("Notification"), wxICON_INFORMATION
| wxOK
,
123 mutex::holder
h(*ui_mutex
);
124 modal_dialog_confirm
= confirm
;
125 modal_dialog_active
= false;
126 ui_condition
->signal();
128 } else if(c
== UISERV_UPDATE_MESSAGES
) {
130 msg_window
->notify_update();
131 } else if(c
== UISERV_UPDATE_STATUS
) {
133 main_window
->notify_update_status();
134 } else if(c
== UISERV_UPDATE_SCREEN
) {
136 main_window
->notify_update();
137 } else if(c
== UISERV_EXIT
) {
139 main_window
->notify_exit();
140 } else if(c
== UISERV_UIFUN
) {
141 std::list
<ui_queue_entry
>::iterator i
;
142 queue_synchronous_fn_warning
= true;
145 mutex::holder
h(*ui_mutex
);
148 i
= ui_queue
.begin();
152 mutex::holder
h(*ui_mutex
);
157 queue_synchronous_fn_warning
= false;
162 ui_services_type
* ui_services
;
164 void post_ui_event(int code
)
166 uiserv_event
uic(code
);
167 wxPostEvent(ui_services
, uic
);
170 void save_configuration()
172 std::string cfg
= get_config_path() + "/lsneswxw.rc";
173 std::ofstream
cfgfile(cfg
.c_str());
175 for(auto i
: keygroup::get_axis_set()) {
176 keygroup
* k
= keygroup::lookup_by_name(i
);
177 auto p
= k
->get_parameters();
178 cfgfile
<< "set-axis " << i
<< " ";
180 case keygroup::KT_DISABLED
: cfgfile
<< "disabled"; break;
181 case keygroup::KT_AXIS_PAIR
: cfgfile
<< "axis"; break;
182 case keygroup::KT_AXIS_PAIR_INVERSE
: cfgfile
<< "axis-inverse"; break;
183 case keygroup::KT_PRESSURE_M0
: cfgfile
<< "pressure-0"; break;
184 case keygroup::KT_PRESSURE_MP
: cfgfile
<< "pressure-+"; break;
185 case keygroup::KT_PRESSURE_0M
: cfgfile
<< "pressure0-"; break;
186 case keygroup::KT_PRESSURE_0P
: cfgfile
<< "pressure0+"; break;
187 case keygroup::KT_PRESSURE_PM
: cfgfile
<< "pressure+-"; break;
188 case keygroup::KT_PRESSURE_P0
: cfgfile
<< "pressure+0"; break;
190 cfgfile
<< " minus=" << p
.cal_left
<< " zero=" << p
.cal_center
<< " plus=" << p
.cal_right
191 << " tolerance=" << p
.cal_tolerance
<< std::endl
;
194 for(auto i
: setting::get_settings_set()) {
195 if(!setting::is_set(i
))
196 cfgfile
<< "unset-setting " << i
<< std::endl
;
198 cfgfile
<< "set-setting " << i
<< " " << setting::get(i
) << std::endl
;
200 for(auto i
: setting::get_invalid_values())
201 cfgfile
<< "set-setting " << i
.first
<< " " << i
.second
<< std::endl
;
203 for(auto i
: command::get_aliases()) {
204 std::string old_alias_value
= command::get_alias_for(i
);
205 while(old_alias_value
!= "") {
206 std::string aliasline
;
207 size_t s
= old_alias_value
.find_first_of("\n");
208 if(s
< old_alias_value
.length()) {
209 aliasline
= old_alias_value
.substr(0, s
);
210 old_alias_value
= old_alias_value
.substr(s
+ 1);
212 aliasline
= old_alias_value
;
213 old_alias_value
= "";
215 cfgfile
<< "alias-command " << i
<< " " << aliasline
<< std::endl
;
219 for(auto i
: keymapper::get_bindings()) {
221 size_t s
= i2
.find_first_of("|");
222 size_t s2
= i2
.find_first_of("/");
223 if(s
> i2
.length() || s2
> s
)
225 std::string key
= i2
.substr(s
+ 1);
226 std::string mod
= i2
.substr(0, s2
);
227 std::string modspec
= i2
.substr(s2
+ 1, s
- s2
- 1);
228 std::string old_command_value
= keymapper::get_command_for(i
);
229 if(mod
!= "" || modspec
!= "")
230 cfgfile
<< "bind-key " << mod
<< "/" << modspec
<< " " << key
<< " "
231 << old_command_value
<< std::endl
;
233 cfgfile
<< "bind-key " << key
<< " " << old_command_value
<< std::endl
;
236 std::ofstream
lsave(get_config_path() + "/" + our_rom_name
+ ".ls");
241 void* eloop_helper(void* x
)
243 platform::dummy_event_loop();
248 wxString
towxstring(const std::string
& str
) throw(std::bad_alloc
)
250 return wxString(str
.c_str(), wxConvUTF8
);
253 std::string
tostdstring(const wxString
& str
) throw(std::bad_alloc
)
255 return std::string(str
.mb_str(wxConvUTF8
));
258 std::string
pick_archive_member(wxWindow
* parent
, const std::string
& filename
) throw(std::bad_alloc
)
260 //Did we pick a .zip file?
263 zip_reader
zr(filename
);
264 std::vector
<wxString
> files
;
266 files
.push_back(towxstring(i
));
267 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(parent
, wxT("Select file within .zip"),
268 wxT("Select member"), files
.size(), &files
[0]);
269 if(d2
->ShowModal() == wxID_CANCEL
) {
273 f
= filename
+ "/" + tostdstring(d2
->GetStringSelection());
282 void signal_program_exit()
284 post_ui_event(UISERV_EXIT
);
287 void signal_resize_needed()
289 post_ui_event(UISERV_RESIZED
);
292 void graphics_plugin::init() throw()
294 initialize_wx_keyboard();
297 void graphics_plugin::quit() throw()
301 static const wxCmdLineEntryDesc dummy_descriptor_table
[] = {
302 { wxCMD_LINE_PARAM
, NULL
, NULL
, NULL
, wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
|
303 wxCMD_LINE_PARAM_MULTIPLE
},
307 class lsnes_app
: public wxApp
311 virtual bool OnInit();
312 virtual int OnExit();
313 virtual void OnInitCmdLine(wxCmdLineParser
& parser
);
314 virtual bool OnCmdLineParsed(wxCmdLineParser
& parser
);
321 IMPLEMENT_APP(lsnes_app
)
323 lsnes_app::lsnes_app()
325 settings_mode
= false;
328 void lsnes_app::OnInitCmdLine(wxCmdLineParser
& parser
)
330 parser
.SetDesc(dummy_descriptor_table
);
331 parser
.SetSwitchChars(wxT(""));
334 bool lsnes_app::OnCmdLineParsed(wxCmdLineParser
& parser
)
336 std::vector
<std::string
> cmdline
;
337 for(size_t i
= 0; i
< parser
.GetParamCount(); i
++)
338 cmdline
.push_back(tostdstring(parser
.GetParam(i
)));
339 for(auto i
: cmdline
) {
341 if(i
== "--settings")
342 settings_mode
= true;
343 if(r
= regex("--rom=(.+)", i
))
345 if(r
= regex("--load=(.+)", i
))
351 bool lsnes_app::OnInit()
357 bring_app_foreground();
359 ui_services
= new ui_services_type();
360 ui_mutex
= &mutex::aquire();
361 ui_condition
= &condition::aquire(*ui_mutex
);
363 bsnes_core_version
= get_core_identifier();
364 ui_thread
= &thread_id::me();
367 messages
<< "BSNES version: " << bsnes_core_version
<< std::endl
;
368 messages
<< "lsnes version: lsnes rr" << lsnes_version
<< std::endl
;
370 controls
.set_port(0, porttype_info::port_default(0), false);
371 controls
.set_port(1, porttype_info::port_default(1), false);
373 std::string cfgpath
= get_config_path();
374 messages
<< "Saving per-user data to: " << get_config_path() << std::endl
;
375 messages
<< "--- Running lsnesrc --- " << std::endl
;
376 setting::set_storage_mode(true);
377 command::invokeC("run-script " + cfgpath
+ "/lsneswxw.rc");
378 setting::set_storage_mode(false);
379 messages
<< "--- End running lsnesrc --- " << std::endl
;
382 //We got to boot this up quite a bit to get the joystick driver working.
383 //In practicular, we need joystick thread and emulator thread in pause.
384 joystick_thread_handle
= &thread::create(joystick_thread
, NULL
);
385 thread
* dummy_loop
= &thread::create(eloop_helper
, NULL
);
386 wxsetingsdialog_display(NULL
, false);
387 platform::exit_dummy_event_loop();
388 joystick_plugin::signal();
389 joystick_thread_handle
->join();
391 save_configuration();
396 joystick_thread_handle
= &thread::create(joystick_thread
, NULL
);
398 msg_window
= new wxwin_messages();
401 do_basic_core_init();
402 loaded_rom
* rom
= NULL
;
406 rom
= new loaded_rom(c_rom
);
407 rom
->load(mov
.movie_rtc_second
, mov
.movie_rtc_subsecond
);
408 } catch(std::exception
& e
) {
409 std::cerr
<< "Can't load ROM: " << e
.what() << std::endl
;
413 rom
= new loaded_rom
;
414 moviefile
* mov
= NULL
;
417 mov
= new moviefile(c_file
);
419 rom
->load(mov
->movie_rtc_second
, mov
->movie_rtc_subsecond
);
420 } catch(std::exception
& e
) {
421 std::cerr
<< "Can't load state: " << e
.what() << std::endl
;
426 mov
->port1
= &porttype_info::port_default(0);
427 mov
->port2
= &porttype_info::port_default(1);
428 mov
->input
.clear(*mov
->port1
, *mov
->port2
);
430 //Initialize the remainder.
431 mov
->coreversion
= bsnes_core_version
;
432 mov
->projectid
= get_random_hexstring(40);
433 mov
->rerecords
= "0";
434 for(size_t i
= 0; i
< sizeof(rom
->romimg
)/sizeof(rom
->romimg
[0]); i
++) {
435 mov
->romimg_sha256
[i
] = rom
->romimg
[i
].sha256
;
436 mov
->romxml_sha256
[i
] = rom
->romxml
[i
].sha256
;
438 mov
->gametype
= &rom
->rtype
->combine_region(*rom
->region
);
441 mov
->start_paused
= true;
442 boot_emulator(*rom
, *mov
);
447 int lsnes_app::OnExit()
451 //NULL these so no further messages will be sent.
453 auto y
= main_window
;
456 save_configuration();
457 information_dispatch::do_dump_end();
459 joystick_plugin::signal();
460 joystick_thread_handle
->join();
465 void graphics_plugin::notify_message() throw()
467 post_ui_event(UISERV_UPDATE_MESSAGES
);
470 void graphics_plugin::notify_status() throw()
472 post_ui_event(UISERV_UPDATE_STATUS
);
475 void graphics_plugin::notify_screen() throw()
477 post_ui_event(UISERV_UPDATE_SCREEN
);
480 bool graphics_plugin::modal_message(const std::string
& text
, bool confirm
) throw()
482 mutex::holder
h(*ui_mutex
);
483 modal_dialog_active
= true;
484 modal_dialog_confirm
= confirm
;
485 modal_dialog_text
= text
;
486 post_ui_event(UISERV_MODAL
);
487 while(modal_dialog_active
)
488 ui_condition
->wait(10000);
489 return modal_dialog_confirm
;
492 void graphics_plugin::fatal_error() throw()
494 //Fun: This can be called from any thread!
495 if(ui_thread
->is_me()) {
497 platform::set_modal_pause(true);
498 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"), wxICON_ERROR
| wxOK
);
500 //Emulation thread panic. Signal the UI thread.
501 post_ui_event(UISERV_PANIC
);
506 void _runuifun_async(void (*fn
)(void*), void* arg
)
508 mutex::holder
h(*ui_mutex
);
512 ui_queue
.push_back(e
);
513 auto i
= ui_queue
.insert(ui_queue
.end(), e
);
514 post_ui_event(UISERV_UIFUN
);
518 canceled_exception::canceled_exception() : std::runtime_error("Dialog canceled") {}
520 std::string
pick_file(wxWindow
* parent
, const std::string
& title
, const std::string
& startdir
, bool forsave
)
522 wxString _title
= towxstring(title
);
523 wxString _startdir
= towxstring(startdir
);
524 wxFileDialog
* d
= new wxFileDialog(parent
, _title
, _startdir
, wxT(""), wxT("All files|*"), forsave
?
525 wxFD_SAVE
: wxFD_OPEN
);
526 if(d
->ShowModal() == wxID_CANCEL
)
527 throw canceled_exception();
528 std::string filename
= tostdstring(d
->GetPath());
531 throw canceled_exception();
535 std::string
pick_file_member(wxWindow
* parent
, const std::string
& title
, const std::string
& startdir
)
537 std::string filename
= pick_file(parent
, title
, startdir
, false);
538 //Did we pick a .zip file?
540 zip_reader
zr(filename
);
541 std::vector
<std::string
> files
;
544 filename
= filename
+ "/" + pick_among(parent
, "Select member", "Select file within .zip", files
);
545 } catch(canceled_exception
& e
) {
546 //Throw these forward.
554 std::string
pick_among(wxWindow
* parent
, const std::string
& title
, const std::string
& prompt
,
555 const std::vector
<std::string
>& choices
)
557 std::vector
<wxString
> _choices
;
558 for(auto i
: choices
)
559 _choices
.push_back(towxstring(i
));
560 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(parent
, towxstring(prompt
), towxstring(title
),
561 _choices
.size(), &_choices
[0]);
562 if(d2
->ShowModal() == wxID_CANCEL
) {
564 throw canceled_exception();
566 std::string out
= tostdstring(d2
->GetStringSelection());
571 std::string
pick_text(wxWindow
* parent
, const std::string
& title
, const std::string
& prompt
, const std::string
& dflt
,
574 wxTextEntryDialog
* d2
= new wxTextEntryDialog(parent
, towxstring(prompt
), towxstring(title
), towxstring(dflt
),
575 wxOK
| wxCANCEL
| wxCENTRE
| (multiline
? wxTE_MULTILINE
: 0));
576 if(d2
->ShowModal() == wxID_CANCEL
) {
578 throw canceled_exception();
580 std::string text
= tostdstring(d2
->GetValue());
585 void show_message_ok(wxWindow
* parent
, const std::string
& title
, const std::string
& text
, int icon
)
587 wxMessageDialog
* d3
= new wxMessageDialog(parent
, towxstring(text
), towxstring(title
), wxOK
| icon
);
593 const char* graphics_plugin::name
= "wxwidgets graphics plugin";