3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/controllerframe.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/framerate.hpp"
9 #include "core/loadlib.hpp"
10 #include "lua/lua.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/memorywatch.hpp"
13 #include "core/misc.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/window.hpp"
16 #include "core/zip.hpp"
21 #include "platform/wxwidgets/menu_dump.hpp"
22 #include "platform/wxwidgets/platform.hpp"
23 #include "platform/wxwidgets/window_mainwindow.hpp"
24 #include "platform/wxwidgets/window_status.hpp"
26 #define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
31 #define UINT64_C(val) val##ULL
33 #include <libswscale/swscale.h>
38 wxID_PAUSE
= wxID_HIGHEST
+ 1,
44 wxID_SHOW_AUDIO_STATUS
,
46 wxID_AUDIODEV_LAST
= wxID_AUDIODEV_FIRST
+ 255,
61 wxID_AUTOHOLD_LAST
= wxID_AUTOHOLD_FIRST
+ 1023,
64 wxID_EDIT_KEYBINDINGS
,
66 wxID_EDIT_MEMORYWATCH
,
67 wxID_SAVE_MEMORYWATCH
,
68 wxID_LOAD_MEMORYWATCH
,
70 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
82 unsigned char* screen_buffer
;
85 bool main_window_dirty
;
86 struct thread
* emulation_thread
;
90 std::string windowname
= "lsnes rr" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
91 return towxstring(windowname
);
96 struct loaded_rom
* rom
;
97 struct moviefile
* initial
;
98 bool load_has_to_succeed
;
101 void* emulator_main(void* _args
)
103 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
106 struct moviefile
* movie
= args
->initial
;
107 bool has_to_succeed
= args
->load_has_to_succeed
;
108 platform::flush_command_queue();
109 main_loop(*our_rom
, *movie
, has_to_succeed
);
110 signal_program_exit();
111 } catch(std::bad_alloc
& e
) {
113 } catch(std::exception
& e
) {
114 messages
<< "FATAL: " << e
.what() << std::endl
;
115 platform::fatal_error();
120 void join_emulator_thread()
122 emulation_thread
->join();
125 keygroup
mouse_x("mouse_x", keygroup::KT_MOUSE
);
126 keygroup
mouse_y("mouse_y", keygroup::KT_MOUSE
);
127 keygroup
mouse_l("mouse_left", keygroup::KT_KEY
);
128 keygroup
mouse_m("mouse_center", keygroup::KT_KEY
);
129 keygroup
mouse_r("mouse_right", keygroup::KT_KEY
);
130 keygroup
mouse_i("mouse_inwindow", keygroup::KT_KEY
);
132 void handle_wx_mouse(wxMouseEvent
& e
)
134 platform::queue(keypress(modifier_set(), mouse_x
, e
.GetX()));
135 platform::queue(keypress(modifier_set(), mouse_y
, e
.GetY()));
137 platform::queue(keypress(modifier_set(), mouse_i
, 1));
139 platform::queue(keypress(modifier_set(), mouse_i
, 0));
141 platform::queue(keypress(modifier_set(), mouse_l
, 1));
143 platform::queue(keypress(modifier_set(), mouse_l
, 0));
145 platform::queue(keypress(modifier_set(), mouse_m
, 1));
147 platform::queue(keypress(modifier_set(), mouse_m
, 0));
149 platform::queue(keypress(modifier_set(), mouse_r
, 1));
151 platform::queue(keypress(modifier_set(), mouse_r
, 0));
154 bool is_readonly_mode()
157 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
161 bool UI_get_autohold(unsigned pid
, unsigned idx
)
164 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
168 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
170 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
173 int UI_controller_index_by_logical(unsigned lid
)
176 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
180 int UI_button_id(unsigned pcid
, unsigned lidx
)
183 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
187 class controller_autohold_menu
: public wxMenu
190 controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
);
193 void on_select(wxCommandEvent
& e
);
194 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
197 wxMenuItem
* entries
[MAX_LOGICAL_BUTTONS
];
198 unsigned enabled_entries
;
201 class autohold_menu
: public wxMenu
204 autohold_menu(wxwin_mainwindow
* win
);
206 void on_select(wxCommandEvent
& e
);
207 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
209 controller_autohold_menu
* menus
[MAXCONTROLLERS
];
210 wxMenuItem
* entries
[MAXCONTROLLERS
];
213 class sound_select_menu
: public wxMenu
216 sound_select_menu(wxwin_mainwindow
* win
);
217 void update(const std::string
& dev
);
218 void on_select(wxCommandEvent
& e
);
220 std::map
<std::string
, wxMenuItem
*> items
;
221 std::map
<int, std::string
> devices
;
224 class sound_select_menu
;
226 class broadcast_listener
: public information_dispatch
229 broadcast_listener(wxwin_mainwindow
* win
);
230 void set_sound_select(sound_select_menu
* sdev
);
231 void set_autohold_menu(autohold_menu
* ah
);
232 void on_sound_unmute(bool unmute
) throw();
233 void on_sound_change(const std::string
& dev
) throw();
234 void on_mode_change(bool readonly
) throw();
235 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
236 void on_autohold_reconfigure();
238 wxwin_mainwindow
* mainw
;
239 sound_select_menu
* sounddev
;
240 autohold_menu
* ahmenu
;
243 controller_autohold_menu::controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
)
245 modal_pause_holder hld
;
247 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
248 int id
= wxID_AUTOHOLD_FIRST
+ MAX_LOGICAL_BUTTONS
* lid
+ i
;
249 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
254 void controller_autohold_menu::change_type()
257 int pid
= controls
.lcid_to_pcid(our_lid
);
258 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
261 pidx
= controls
.button_id(pid
, i
);
263 entries
[i
]->Check(pid
> 0 && UI_get_autohold(pid
, pidx
));
264 entries
[i
]->Enable();
267 entries
[i
]->Check(false);
268 entries
[i
]->Enable(false);
273 bool controller_autohold_menu::is_dummy()
275 return !enabled_entries
;
278 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
281 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* MAX_LOGICAL_BUTTONS
|| x
>= wxID_AUTOHOLD_FIRST
*
282 (our_lid
+ 1) * MAX_LOGICAL_BUTTONS
) {
285 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % MAX_LOGICAL_BUTTONS
;
286 modal_pause_holder hld
;
287 int pid
= controls
.lcid_to_pcid(our_lid
);
288 if(pid
< 0 || !entries
[lidx
])
290 int pidx
= controls
.button_id(pid
, lidx
);
293 //Autohold change on pid=pid, ctrlindx=idx, state
294 bool newstate
= entries
[lidx
]->IsChecked();
295 UI_change_autohold(pid
, pidx
, newstate
);
298 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
300 modal_pause_holder hld
;
301 int pid2
= UI_controller_index_by_logical(our_lid
);
302 if(pid2
< 0 || static_cast<unsigned>(pid
) != pid2
)
304 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
305 int idx
= UI_button_id(pid2
, i
);
306 if(idx
< 0 || static_cast<unsigned>(idx
) != ctrlnum
)
308 entries
[i
]->Check(newstate
);
313 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
315 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
316 std::ostringstream str
;
317 str
<< "Controller #&" << (i
+ 1);
318 menus
[i
] = new controller_autohold_menu(i
, DT_NONE
);
319 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
320 entries
[i
]->Enable(!menus
[i
]->is_dummy());
322 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
323 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
327 void autohold_menu::reconfigure()
329 modal_pause_holder hld
;
330 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
331 menus
[i
]->change_type();
332 entries
[i
]->Enable(!menus
[i
]->is_dummy());
336 void autohold_menu::on_select(wxCommandEvent
& e
)
338 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
339 menus
[i
]->on_select(e
);
342 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
344 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
345 menus
[i
]->update(pid
, ctrlnum
, newstate
);
348 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
350 std::string curdev
= platform::get_sound_device();
351 int j
= wxID_AUDIODEV_FIRST
;
352 for(auto i
: platform::get_sound_devices()) {
353 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
354 devices
[j
] = i
.first
;
355 if(i
.first
== curdev
)
356 items
[i
.first
]->Check();
357 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
358 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
363 void sound_select_menu::update(const std::string
& dev
)
368 void sound_select_menu::on_select(wxCommandEvent
& e
)
370 std::string devname
= devices
[e
.GetId()];
372 runemufn([devname
]() { platform::set_sound_device(devname
); });
375 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
376 : information_dispatch("wxwidgets-broadcast-listener")
381 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
386 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
391 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
393 runuifun([unmute
, mainw
]() { mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
396 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
398 runuifun([dev
, sounddev
]() { if(sounddev
) sounddev
->update(dev
); });
401 void broadcast_listener::on_mode_change(bool readonly
) throw()
403 runuifun([readonly
, mainw
]() { mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
406 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
408 runuifun([pid
, ctrlnum
, newstate
, ahmenu
]() { ahmenu
->update(pid
, ctrlnum
, newstate
); });
411 void broadcast_listener::on_autohold_reconfigure()
413 runuifun([ahmenu
]() { ahmenu
->reconfigure(); });
417 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
420 struct emu_args
* a
= new emu_args
;
423 a
->load_has_to_succeed
= false;
424 modal_pause_holder hld
;
425 emulation_thread
= &thread::create(emulator_main
, a
);
426 main_window
= new wxwin_mainwindow();
428 } catch(std::bad_alloc
& e
) {
433 wxwin_mainwindow::panel::panel(wxWindow
* win
)
436 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
437 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
438 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
439 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
440 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
441 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
442 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
443 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
444 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
445 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
446 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
447 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
448 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
449 SetMinSize(wxSize(512, 448));
452 void wxwin_mainwindow::menu_start(wxString name
)
454 while(!upper
.empty())
456 current_menu
= new wxMenu();
457 menubar
->Append(current_menu
, name
);
460 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
462 while(!upper
.empty())
464 menubar
->Append(menu
, name
);
468 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
470 current_menu
->AppendSubMenu(menu
, name
);
473 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
475 current_menu
->Append(id
, name
);
476 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
477 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
480 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
482 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
483 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
484 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
487 void wxwin_mainwindow::menu_start_sub(wxString name
)
489 wxMenu
* old
= current_menu
;
490 upper
.push(current_menu
);
491 current_menu
= new wxMenu();
492 old
->AppendSubMenu(current_menu
, name
);
495 void wxwin_mainwindow::menu_end_sub(wxString name
)
497 current_menu
= upper
.top();
501 bool wxwin_mainwindow::menu_ischecked(int id
)
503 if(checkitems
.count(id
))
504 return checkitems
[id
]->IsChecked();
509 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
511 if(checkitems
.count(id
))
512 return checkitems
[id
]->Check(newstate
);
517 void wxwin_mainwindow::menu_separator()
519 current_menu
->AppendSeparator();
522 void wxwin_mainwindow::panel::request_paint()
527 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
529 render_framebuffer();
530 static struct SwsContext
* ctx
;
536 if(!screen_buffer
|| main_screen
.width
!= old_width
|| main_screen
.height
!= old_height
) {
538 delete[] screen_buffer
;
539 screen_buffer
= new unsigned char[main_screen
.width
* main_screen
.height
* 3];
540 old_height
= main_screen
.height
;
541 old_width
= main_screen
.width
;
542 uint32_t w
= main_screen
.width
;
543 uint32_t h
= main_screen
.height
;
545 ctx
= sws_getCachedContext(ctx
, w
, h
, PIX_FMT_RGBA
, w
, h
, PIX_FMT_BGR24
, SWS_POINT
|
546 SWS_CPU_CAPS_MMX2
, NULL
, NULL
, NULL
);
551 SetMinSize(wxSize(w
, h
));
554 srcs
[0] = 4 * main_screen
.width
;
555 dsts
[0] = 3 * main_screen
.width
;
556 srcp
[0] = reinterpret_cast<unsigned char*>(main_screen
.memory
);
557 dstp
[0] = screen_buffer
;
558 memset(screen_buffer
, 0, main_screen
.width
* main_screen
.height
* 3);
559 uint64_t t1
= get_utime();
560 if(main_screen
.width
&& main_screen
.height
)
561 sws_scale(ctx
, srcp
, srcs
, 0, main_screen
.height
, dstp
, dsts
);
562 uint64_t t2
= get_utime();
563 wxBitmap
bmp(wxImage(main_screen
.width
, main_screen
.height
, screen_buffer
, true));
564 uint64_t t3
= get_utime();
565 dc
.DrawBitmap(bmp
, 0, 0, false);
566 main_window_dirty
= false;
569 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
574 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
576 handle_wx_keyboard(e
, true);
579 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
581 handle_wx_keyboard(e
, false);
584 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
589 wxwin_mainwindow::wxwin_mainwindow()
590 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
591 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
593 broadcast_listener
* blistener
= new broadcast_listener(this);
595 wxFlexGridSizer
* toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
596 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
597 toplevel
->Add(spanel
= new wxwin_status::panel(this, 20), 1, wxGROW
);
598 toplevel
->SetSizeHints(this);
602 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
603 menubar
= new wxMenuBar
;
606 //TOP-level accels: ACFOS.
607 //System menu: (ACFOS)EMNPQRU
608 menu_start(wxT("&System"));
609 menu_entry(wxID_FRAMEADVANCE
, wxT("Fra&me advance"));
610 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("S&ubframe advance"));
611 menu_entry(wxID_NEXTPOLL
, wxT("&Next poll"));
612 menu_entry(wxID_PAUSE
, wxT("&Pause/Unpause"));
614 menu_entry(wxID_ERESET
, wxT("&Reset"));
616 menu_entry(wxID_EDIT_AUTHORS
, wxT("&Edit game name && authors"));
618 menu_entry(wxID_EXIT
, wxT("&Quit"));
620 menu_entry(wxID_ABOUT
, wxT("About"));
621 //File menu: (ACFOS)DEILMNPRTUVW
622 menu_start(wxT("&File"));
623 menu_entry_check(wxID_READONLY_MODE
, wxT("Reado&nly mode"));
624 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
626 menu_entry(wxID_SAVE_STATE
, wxT("Save stat&e"));
627 menu_entry(wxID_SAVE_MOVIE
, wxT("Sa&ve movie"));
629 menu_entry(wxID_LOAD_STATE
, wxT("&Load state"));
630 menu_entry(wxID_LOAD_STATE_RO
, wxT("Loa&d state (readonly)"));
631 menu_entry(wxID_LOAD_STATE_RW
, wxT("Load s&tate (read-write)"));
632 menu_entry(wxID_LOAD_STATE_P
, wxT("Load state (&preserve)"));
633 menu_entry(wxID_LOAD_MOVIE
, wxT("Load &movie"));
634 menu_entry(wxID_REWIND_MOVIE
, wxT("Re&wind movie"));
636 menu_entry(wxID_CANCEL_SAVES
, wxT("Cancel pend&ing saves"));
638 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Save sc&reenshot"));
640 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
641 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
642 if(load_library_supported
) {
644 menu_entry(wxID_LOAD_LIBRARY
, towxstring(std::string("Load ") + library_is_called
));
646 //Autohold menu: (ACFOS)
647 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
648 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
649 //Scripting menu: (ACFOS)ERU
650 menu_start(wxT("S&cripting"));
651 menu_entry(wxID_RUN_SCRIPT
, wxT("&Run script"));
654 menu_entry(wxID_EVAL_LUA
, wxT("&Evaluate Lua statement"));
655 menu_entry(wxID_RUN_LUA
, wxT("R&un Lua script"));
658 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch"));
660 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch"));
661 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch"));
663 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search"));
664 //Settings menu: (ACFOS)
665 menu_start(wxT("Settings"));
666 menu_entry(wxID_EDIT_AXES
, wxT("Configure axes"));
667 menu_entry(wxID_EDIT_SETTINGS
, wxT("Configure settings"));
668 menu_entry(wxID_EDIT_HOTKEYS
, wxT("Configure hotkeys"));
669 menu_entry(wxID_EDIT_KEYBINDINGS
, wxT("Configure keybindings"));
670 menu_entry(wxID_EDIT_ALIAS
, wxT("Configure aliases"));
671 menu_entry(wxID_EDIT_JUKEBOX
, wxT("Configure jukebox"));
672 if(platform::sound_initialized()) {
673 //Sound menu: (ACFOS)EHU
674 menu_start(wxT("S&ound"));
675 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("So&unds enabled"));
676 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
677 menu_entry(wxID_SHOW_AUDIO_STATUS
, wxT("S&how audio status"));
679 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
680 new sound_select_menu(this)));
681 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
685 void wxwin_mainwindow::request_paint()
690 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
692 //Veto it for now, latter things will delete it.
694 platform::queue("quit-emulator");
697 void wxwin_mainwindow::notify_update() throw()
699 if(!main_window_dirty
) {
700 main_window_dirty
= true;
705 void wxwin_mainwindow::notify_update_status() throw()
708 spanel
->dirty
= true;
713 void wxwin_mainwindow::notify_exit() throw()
715 join_emulator_thread();
719 #define NEW_KEYBINDING "A new binding..."
720 #define NEW_ALIAS "A new alias..."
721 #define NEW_WATCH "A new watch..."
723 void strip_CR(std::string
& x
) throw(std::bad_alloc
);
725 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
728 handle_menu_click_cancelable(e
);
729 } catch(canceled_exception
& e
) {
731 } catch(std::bad_alloc
& e
) {
733 } catch(std::exception
& e
) {
734 show_message_ok(this, "Error in menu handler", e
.what(), wxICON_EXCLAMATION
);
738 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent
& e
)
740 std::string filename
;
743 case wxID_FRAMEADVANCE
:
744 platform::queue("+advance-frame");
745 platform::queue("-advance-frame");
747 case wxID_SUBFRAMEADVANCE
:
748 platform::queue("+advance-poll");
749 platform::queue("-advance-poll");
752 platform::queue("advance-skiplag");
755 platform::queue("pause-emulator");
758 platform::queue("reset");
761 platform::queue("quit-emulator");
763 case wxID_AUDIO_ENABLED
:
764 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
766 case wxID_SHOW_AUDIO_STATUS
:
767 platform::queue("show-sound-status");
769 case wxID_CANCEL_SAVES
:
770 platform::queue("cancel-saves");
772 case wxID_LOAD_MOVIE
:
773 platform::queue("load-movie " + pick_file(this, "Load Movie", "."));
775 case wxID_LOAD_STATE
:
776 platform::queue("load " + pick_file(this, "Load State", "."));
778 case wxID_LOAD_STATE_RO
:
779 platform::queue("load-readonly " + pick_file(this, "Load State (Read-Only)", "."));
781 case wxID_LOAD_STATE_RW
:
782 platform::queue("load-state " + pick_file(this, "Load State (Read-Write)", "."));
784 case wxID_LOAD_STATE_P
:
785 platform::queue("load-preserve " + pick_file(this, "Load State (Preserve)", "."));
787 case wxID_REWIND_MOVIE
:
788 platform::queue("rewind-movie");
790 case wxID_SAVE_MOVIE
:
791 platform::queue("save-movie " + pick_file(this, "Save Movie", "."));
793 case wxID_SAVE_STATE
:
794 platform::queue("save-state " + pick_file(this, "Save State", "."));
796 case wxID_SAVE_SCREENSHOT
:
797 platform::queue("take-screenshot " + pick_file(this, "Save State", "."));
799 case wxID_RUN_SCRIPT
:
800 platform::queue("run-script " + pick_file_member(this, "Select Script", "."));
803 platform::queue("run-lua " + pick_file(this, "Select Lua Script", "."));
806 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
808 case wxID_READONLY_MODE
:
809 s
= menu_ischecked(wxID_READONLY_MODE
);
811 movb
.get_movie().readonly_mode(s
);
813 lua_callback_do_readwrite();
814 update_movie_state();
818 wxeditor_axes_display(this);
820 case wxID_EDIT_AUTHORS
:
821 wxeditor_authors_display(this);
823 case wxID_EDIT_SETTINGS
:
824 wxeditor_settings_display(this);
826 case wxID_EDIT_HOTKEYS
:
827 wxeditor_hotkeys_display(this);
829 case wxID_EDIT_KEYBINDINGS
: {
830 modal_pause_holder hld
;
831 std::set
<std::string
> bind
;
832 runemufn([&bind
]() { bind
= keymapper::get_bindings(); });
833 std::vector
<std::string
> choices
;
834 choices
.push_back(NEW_KEYBINDING
);
836 choices
.push_back(i
);
837 std::string key
= pick_among(this, "Select binding", "Select keybinding to edit", choices
);
838 if(key
== NEW_KEYBINDING
)
839 key
= wxeditor_keyselect(this, false);
840 std::string old_command_value
;
841 runemufn([&old_command_value
, key
]() { old_command_value
= keymapper::get_command_for(key
); });
842 std::string newcommand
= pick_text(this, "Edit binding", "Enter new command for binding:",
845 std::string faulttext
;
846 runemufn([&fault
, &faulttext
, key
, newcommand
]() {
848 keymapper::bind_for(key
, newcommand
);
849 } catch(std::exception
& e
) {
851 faulttext
= e
.what();
855 show_message_ok(this, "Error", "Can't bind key: " + faulttext
, wxICON_EXCLAMATION
);
858 case wxID_EDIT_ALIAS
: {
859 modal_pause_holder hld
;
860 std::set
<std::string
> bind
;
861 runemufn([&bind
]() { bind
= command::get_aliases(); });
862 std::vector
<std::string
> choices
;
863 choices
.push_back(NEW_ALIAS
);
865 choices
.push_back(i
);
866 std::string alias
= pick_among(this, "Select alias", "Select alias to edit", choices
);
867 if(alias
== NEW_ALIAS
) {
868 alias
= pick_text(this, "Enter alias name", "Enter name for the new alias:");
869 if(!command::valid_alias_name(alias
)) {
870 show_message_ok(this, "Error", "Not a valid alias name: " + alias
,
872 throw canceled_exception();
875 std::string old_alias_value
;
876 runemufn([alias
, &old_alias_value
]() { old_alias_value
= command::get_alias_for(alias
); });
877 std::string newcmd
= pick_text(this, "Edit alias", "Enter new commands for alias:",
878 old_alias_value
, true);
879 runemufn([alias
, newcmd
]() { command::set_alias_for(alias
, newcmd
); });
882 case wxID_EDIT_JUKEBOX
: {
883 modal_pause_holder hld
;
884 std::vector
<std::string
> new_jukebox
;
887 for(auto i
: get_jukebox_names())
890 x
= pick_text(this, "Configure jukebox", "List jukebox entries", x
, true);
892 size_t split
= x
.find_first_of("\n");
894 if(split
< x
.length()) {
895 l
= x
.substr(0, split
);
896 x
= x
.substr(split
+ 1);
903 new_jukebox
.push_back(l
);
905 runemufn([&new_jukebox
]() { set_jukebox_names(new_jukebox
); });
906 notify_update_status();
909 case wxID_EDIT_MEMORYWATCH
: {
910 modal_pause_holder hld
;
911 std::set
<std::string
> bind
;
912 runemufn([&bind
]() { bind
= get_watches(); });
913 std::vector
<std::string
> choices
;
914 choices
.push_back(NEW_WATCH
);
916 choices
.push_back(i
);
917 std::string watch
= pick_among(this, "Select watch", "Select watch to edit", choices
);
918 if(watch
== NEW_WATCH
)
919 watch
= pick_text(this, "Enter watch name", "Enter name for the new watch:");
920 std::string newexpr
= pick_text(this, "Edit watch", "Enter new expression for watch:",
921 get_watchexpr_for(watch
));
922 runemufn([watch
, newexpr
]() { set_watchexpr_for(watch
, newexpr
); });
925 case wxID_SAVE_MEMORYWATCH
: {
926 modal_pause_holder hld
;
927 std::set
<std::string
> old_watches
;
928 runemufn([&old_watches
]() { old_watches
= get_watches(); });
929 std::string filename
= pick_file(this, "Save watches to file", ".");
930 std::ofstream
out(filename
.c_str());
931 for(auto i
: old_watches
) {
933 runemufn([i
, &val
]() { val
= get_watchexpr_for(i
); });
934 out
<< i
<< std::endl
<< val
<< std::endl
;
939 case wxID_LOAD_MEMORYWATCH
: {
940 modal_pause_holder hld
;
941 std::set
<std::string
> old_watches
;
942 runemufn([&old_watches
]() { old_watches
= get_watches(); });
943 std::map
<std::string
, std::string
> new_watches
;
944 std::string filename
= pick_file_member(this, "Choose memory watch file", ".");
947 std::istream
& in
= open_file_relative(filename
, "");
951 std::getline(in
, wname
);
952 std::getline(in
, wexpr
);
953 new_watches
[wname
] = wexpr
;
956 } catch(std::exception
& e
) {
957 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e
.what(),
962 runemufn([&new_watches
, &old_watches
]() {
963 for(auto i
: new_watches
)
964 set_watchexpr_for(i
.first
, i
.second
);
965 for(auto i
: old_watches
)
966 if(!new_watches
.count(i
))
967 set_watchexpr_for(i
, "");
971 case wxID_MEMORY_SEARCH
:
972 wxwindow_memorysearch_display();
975 std::ostringstream str
;
976 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
977 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
978 str
<< "Core: " << bsnes_core_version
<< std::endl
;
979 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
982 case wxID_LOAD_LIBRARY
: {
983 std::string name
= std::string("load ") + library_is_called
;
984 load_library(pick_file(this, name
, "."));