3 #include "core/emucore.hpp"
5 #include "core/command.hpp"
6 #include "core/controller.hpp"
7 #include "core/controllerframe.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/framebuffer.hpp"
10 #include "core/framerate.hpp"
11 #include "core/loadlib.hpp"
12 #include "lua/lua.hpp"
13 #include "core/mainloop.hpp"
14 #include "core/memorywatch.hpp"
15 #include "core/misc.hpp"
16 #include "core/moviedata.hpp"
17 #include "core/settings.hpp"
18 #include "core/window.hpp"
19 #include "library/minmax.hpp"
20 #include "library/string.hpp"
21 #include "library/zip.hpp"
29 #include "platform/wxwidgets/menu_dump.hpp"
30 #include "platform/wxwidgets/platform.hpp"
31 #include "platform/wxwidgets/window_mainwindow.hpp"
32 #include "platform/wxwidgets/window_messages.hpp"
33 #include "platform/wxwidgets/window_status.hpp"
38 #define UINT64_C(val) val##ULL
40 #include <libswscale/swscale.h>
45 wxID_PAUSE
= wxID_HIGHEST
+ 1,
51 wxID_SHOW_AUDIO_STATUS
,
53 wxID_AUDIODEV_LAST
= wxID_AUDIODEV_FIRST
+ 255,
70 wxID_AUTOHOLD_LAST
= wxID_AUTOHOLD_FIRST
+ 1023,
71 wxID_EDIT_MEMORYWATCH
,
72 wxID_SAVE_MEMORYWATCH
,
73 wxID_LOAD_MEMORYWATCH
,
76 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
99 wxID_SETTINGS_HOTKEYS
,
100 wxID_RELOAD_ROM_IMAGE
,
107 double horizontal_scale_factor
= 1.0;
108 double vertical_scale_factor
= 1.0;
109 int scaling_flags
= SWS_POINT
;
113 std::string last_volume
= "0dB";
114 unsigned char* screen_buffer
;
117 int old_flags
= SWS_POINT
;
118 bool main_window_dirty
;
119 struct thread
* emulation_thread
;
123 std::string windowname
= "lsnes rr" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
124 return towxstring(windowname
);
129 struct loaded_rom
* rom
;
130 struct moviefile
* initial
;
131 bool load_has_to_succeed
;
134 void* emulator_main(void* _args
)
136 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
139 struct moviefile
* movie
= args
->initial
;
140 bool has_to_succeed
= args
->load_has_to_succeed
;
141 platform::flush_command_queue();
142 main_loop(*our_rom
, *movie
, has_to_succeed
);
143 signal_program_exit();
144 } catch(std::bad_alloc
& e
) {
146 } catch(std::exception
& e
) {
147 messages
<< "FATAL: " << e
.what() << std::endl
;
148 platform::fatal_error();
153 void join_emulator_thread()
155 emulation_thread
->join();
158 keygroup
mouse_x("mouse_x", "mouse", keygroup::KT_MOUSE
);
159 keygroup
mouse_y("mouse_y", "mouse", keygroup::KT_MOUSE
);
160 keygroup
mouse_l("mouse_left", "mouse", keygroup::KT_KEY
);
161 keygroup
mouse_m("mouse_center", "mouse", keygroup::KT_KEY
);
162 keygroup
mouse_r("mouse_right", "mouse", keygroup::KT_KEY
);
163 keygroup
mouse_i("mouse_inwindow", "mouse", keygroup::KT_KEY
);
165 void handle_wx_mouse(wxMouseEvent
& e
)
167 platform::queue(keypress(modifier_set(), mouse_x
, e
.GetX() / horizontal_scale_factor
));
168 platform::queue(keypress(modifier_set(), mouse_y
, e
.GetY() / vertical_scale_factor
));
170 platform::queue(keypress(modifier_set(), mouse_i
, 1));
172 platform::queue(keypress(modifier_set(), mouse_i
, 0));
174 platform::queue(keypress(modifier_set(), mouse_l
, 1));
176 platform::queue(keypress(modifier_set(), mouse_l
, 0));
178 platform::queue(keypress(modifier_set(), mouse_m
, 1));
180 platform::queue(keypress(modifier_set(), mouse_m
, 0));
182 platform::queue(keypress(modifier_set(), mouse_r
, 1));
184 platform::queue(keypress(modifier_set(), mouse_r
, 0));
187 bool is_readonly_mode()
190 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
194 bool UI_get_autohold(unsigned pid
, unsigned idx
)
197 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
201 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
203 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
206 int UI_controller_index_by_logical(unsigned lid
)
209 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
213 int UI_button_id(unsigned pcid
, unsigned lidx
)
216 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
220 void set_speed(double target
)
222 std::string v
= (stringfmt() << target
).str();
224 setting::set("targetfps", "infinite");
226 setting::set("targetfps", v
);
229 class controller_autohold_menu
: public wxMenu
232 controller_autohold_menu(unsigned lid
);
235 void on_select(wxCommandEvent
& e
);
236 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
240 std::vector
<wxMenuItem
*> entries
;
241 unsigned enabled_entries
;
242 std::map
<unsigned, int> pidxs
;
243 std::vector
<bool> autoholds
;
246 class autohold_menu
: public wxMenu
249 autohold_menu(wxwin_mainwindow
* win
);
251 void on_select(wxCommandEvent
& e
);
252 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
254 std::vector
<controller_autohold_menu
*> menus
;
255 std::vector
<wxMenuItem
*> entries
;
258 class sound_select_menu
: public wxMenu
261 sound_select_menu(wxwin_mainwindow
* win
);
262 void update(const std::string
& dev
);
263 void on_select(wxCommandEvent
& e
);
265 std::map
<std::string
, wxMenuItem
*> items
;
266 std::map
<int, std::string
> devices
;
269 class sound_select_menu
;
271 class broadcast_listener
: public information_dispatch
274 broadcast_listener(wxwin_mainwindow
* win
);
275 void set_sound_select(sound_select_menu
* sdev
);
276 void set_autohold_menu(autohold_menu
* ah
);
277 void on_sound_unmute(bool unmute
) throw();
278 void on_sound_change(const std::string
& dev
) throw();
279 void on_mode_change(bool readonly
) throw();
280 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
281 void on_autohold_reconfigure();
283 wxwin_mainwindow
* mainw
;
284 sound_select_menu
* sounddev
;
285 autohold_menu
* ahmenu
;
288 controller_autohold_menu::controller_autohold_menu(unsigned lid
)
290 auto limits
= get_core_logical_controller_limits();
291 entries
.resize(limits
.second
);
292 modal_pause_holder hld
;
294 for(unsigned i
= 0; i
< limits
.second
; i
++) {
295 int id
= wxID_AUTOHOLD_FIRST
+ limits
.second
* lid
+ i
;
296 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
301 void controller_autohold_menu::change_type()
304 runuifun([&autoholds
, &pidxs
, our_pid
]() {
305 auto limits
= get_core_logical_controller_limits();
306 autoholds
.resize(limits
.second
);
307 for(unsigned i
= 0; i
< limits
.second
; i
++) {
310 pidxs
[i
] = controls
.button_id(our_pid
, i
);
312 autoholds
[i
] = (our_pid
> 0 && controls
.autohold(our_pid
, pidxs
[i
]));
314 autoholds
[i
] = false;
317 our_pid
= controls
.lcid_to_pcid(our_lid
);
318 for(auto i
: pidxs
) {
320 entries
[i
.first
]->Check(autoholds
[i
.first
]);
321 entries
[i
.first
]->Enable();
324 entries
[i
.first
]->Check(false);
325 entries
[i
.first
]->Enable(false);
330 bool controller_autohold_menu::is_dummy()
332 return !enabled_entries
;
335 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
337 auto limits
= get_core_logical_controller_limits();
339 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* limits
.second
|| x
>= wxID_AUTOHOLD_FIRST
*
340 (our_lid
+ 1) * limits
.second
) {
343 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % limits
.second
;
344 modal_pause_holder hld
;
345 int pid
= controls
.lcid_to_pcid(our_lid
);
346 if(pid
< 0 || !entries
[lidx
])
348 int pidx
= controls
.button_id(pid
, lidx
);
351 //Autohold change on pid=pid, ctrlindx=idx, state
352 bool newstate
= entries
[lidx
]->IsChecked();
353 UI_change_autohold(pid
, pidx
, newstate
);
356 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
358 modal_pause_holder hld
;
359 if(our_pid
< 0 || static_cast<unsigned>(pid
) != our_pid
)
361 auto limits
= get_core_logical_controller_limits();
362 for(unsigned i
= 0; i
< limits
.second
; i
++) {
363 if(pidxs
[i
] < 0 || static_cast<unsigned>(pidxs
[i
]) != ctrlnum
)
365 entries
[i
]->Check(newstate
);
370 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
372 auto limits
= get_core_logical_controller_limits();
373 entries
.resize(limits
.first
);
374 menus
.resize(limits
.first
);
375 for(unsigned i
= 0; i
< limits
.first
; i
++) {
376 std::ostringstream str
;
377 str
<< "Controller #&" << (i
+ 1);
378 menus
[i
] = new controller_autohold_menu(i
);
379 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
380 entries
[i
]->Enable(!menus
[i
]->is_dummy());
382 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
383 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
387 void autohold_menu::reconfigure()
389 modal_pause_holder hld
;
390 auto limits
= get_core_logical_controller_limits();
391 for(unsigned i
= 0; i
< limits
.first
; i
++) {
392 menus
[i
]->change_type();
393 entries
[i
]->Enable(!menus
[i
]->is_dummy());
397 void autohold_menu::on_select(wxCommandEvent
& e
)
399 auto limits
= get_core_logical_controller_limits();
400 for(unsigned i
= 0; i
< limits
.first
; i
++)
401 menus
[i
]->on_select(e
);
404 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
406 auto limits
= get_core_logical_controller_limits();
407 for(unsigned i
= 0; i
< limits
.first
; i
++)
408 menus
[i
]->update(pid
, ctrlnum
, newstate
);
411 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
413 std::string curdev
= platform::get_sound_device();
414 int j
= wxID_AUDIODEV_FIRST
;
415 for(auto i
: platform::get_sound_devices()) {
416 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
417 devices
[j
] = i
.first
;
418 if(i
.first
== curdev
)
419 items
[i
.first
]->Check();
420 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
421 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
426 void sound_select_menu::update(const std::string
& dev
)
431 void sound_select_menu::on_select(wxCommandEvent
& e
)
433 std::string devname
= devices
[e
.GetId()];
435 runemufn([devname
]() { platform::set_sound_device(devname
); });
438 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
439 : information_dispatch("wxwidgets-broadcast-listener")
444 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
449 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
454 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
456 runuifun([unmute
, mainw
]() { mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
459 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
461 runuifun([dev
, sounddev
]() { if(sounddev
) sounddev
->update(dev
); });
464 void broadcast_listener::on_mode_change(bool readonly
) throw()
466 runuifun([readonly
, mainw
]() { mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
469 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
471 runuifun([pid
, ctrlnum
, newstate
, ahmenu
]() { ahmenu
->update(pid
, ctrlnum
, newstate
); });
474 void broadcast_listener::on_autohold_reconfigure()
476 runuifun([ahmenu
]() { ahmenu
->reconfigure(); });
479 path_setting
moviepath_setting("moviepath");
480 path_setting
rompath_setting("rompath");
482 std::string
movie_path()
484 return setting::get("moviepath");
487 std::string
rom_path()
489 return setting::get("rompath");
492 bool is_lsnes_movie(const std::string
& filename
)
495 zip_reader
r(filename
);
496 std::istream
& s
= r
["systemid"];
501 return (s2
== "lsnes-rr1");
507 class loadfile
: public wxFileDropTarget
510 bool OnDropFiles(wxCoord x
, wxCoord y
, const wxArrayString
& filenames
)
512 if(filenames
.Count() != 1)
514 if(is_lsnes_movie(tostdstring(filenames
[0])))
515 platform::queue("load-smart " + tostdstring(filenames
[0]));
517 platform::queue("unpause-emulator");
518 platform::queue("reload-rom " + tostdstring(filenames
[0]));
525 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
528 struct emu_args
* a
= new emu_args
;
531 a
->load_has_to_succeed
= false;
532 modal_pause_holder hld
;
533 emulation_thread
= &thread::create(emulator_main
, a
);
534 main_window
= new wxwin_mainwindow();
536 } catch(std::bad_alloc
& e
) {
541 wxwin_mainwindow::panel::panel(wxWindow
* win
)
542 : wxPanel(win
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
)
544 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
545 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
546 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
547 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
548 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
549 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
550 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
551 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
552 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
553 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
554 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
555 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
556 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
557 SetMinSize(wxSize(512, 448));
560 void wxwin_mainwindow::menu_start(wxString name
)
562 while(!upper
.empty())
564 current_menu
= new wxMenu();
565 menubar
->Append(current_menu
, name
);
568 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
570 while(!upper
.empty())
572 menubar
->Append(menu
, name
);
576 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
578 current_menu
->AppendSubMenu(menu
, name
);
581 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
583 current_menu
->Append(id
, name
);
584 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
585 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
588 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
590 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
591 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
592 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
595 void wxwin_mainwindow::menu_start_sub(wxString name
)
597 wxMenu
* old
= current_menu
;
598 upper
.push(current_menu
);
599 current_menu
= new wxMenu();
600 old
->AppendSubMenu(current_menu
, name
);
603 void wxwin_mainwindow::menu_end_sub()
605 current_menu
= upper
.top();
609 bool wxwin_mainwindow::menu_ischecked(int id
)
611 if(checkitems
.count(id
))
612 return checkitems
[id
]->IsChecked();
617 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
619 if(checkitems
.count(id
))
620 return checkitems
[id
]->Check(newstate
);
625 void wxwin_mainwindow::menu_separator()
627 current_menu
->AppendSeparator();
630 void wxwin_mainwindow::panel::request_paint()
635 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
637 render_framebuffer();
638 static struct SwsContext
* ctx
;
644 uint32_t tw
= main_screen
.get_width() * horizontal_scale_factor
+ 0.5;
645 uint32_t th
= main_screen
.get_height() * vertical_scale_factor
+ 0.5;
647 main_window_dirty
= false;
650 if(!screen_buffer
|| tw
!= old_width
|| th
!= old_height
|| scaling_flags
!= old_flags
) {
652 delete[] screen_buffer
;
655 old_flags
= scaling_flags
;
656 uint32_t w
= main_screen
.get_width();
657 uint32_t h
= main_screen
.get_height();
659 ctx
= sws_getCachedContext(ctx
, w
, h
, PIX_FMT_RGBA
, tw
, th
, PIX_FMT_BGR24
, scaling_flags
,
661 tw
= max(tw
, static_cast<uint32_t>(128));
662 th
= max(th
, static_cast<uint32_t>(112));
663 screen_buffer
= new unsigned char[tw
* th
* 3];
664 SetMinSize(wxSize(tw
, th
));
665 signal_resize_needed();
667 srcs
[0] = 4 * main_screen
.get_width();
669 srcp
[0] = reinterpret_cast<unsigned char*>(main_screen
.rowptr(0));
670 dstp
[0] = screen_buffer
;
671 memset(screen_buffer
, 0, tw
* th
* 3);
672 if(main_screen
.get_width() && main_screen
.get_height())
673 sws_scale(ctx
, srcp
, srcs
, 0, main_screen
.get_height(), dstp
, dsts
);
674 wxBitmap
bmp(wxImage(tw
, th
, screen_buffer
, true));
675 dc
.DrawBitmap(bmp
, 0, 0, false);
676 main_window_dirty
= false;
679 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
684 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
686 handle_wx_keyboard(e
, true);
689 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
691 handle_wx_keyboard(e
, false);
694 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
699 wxwin_mainwindow::wxwin_mainwindow()
700 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
701 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
703 broadcast_listener
* blistener
= new broadcast_listener(this);
705 toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
706 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
707 toplevel
->Add(spanel
= new wxwin_status::panel(this, gpanel
, 20), 1, wxGROW
);
709 toplevel
->SetSizeHints(this);
713 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
714 menubar
= new wxMenuBar
;
717 menu_start(wxT("File"));
718 menu_start_sub(wxT("New"));
719 menu_entry(wxID_NEW_MOVIE
, wxT("Movie..."));
721 menu_start_sub(wxT("Load"));
722 menu_entry(wxID_LOAD_STATE
, wxT("State..."));
723 menu_entry(wxID_LOAD_STATE_RO
, wxT("State (readonly)..."));
724 menu_entry(wxID_LOAD_STATE_RW
, wxT("State (read-write)..."));
725 menu_entry(wxID_LOAD_STATE_P
, wxT("State (preserve input)..."));
726 menu_entry(wxID_LOAD_MOVIE
, wxT("Movie..."));
727 if(load_library_supported
) {
729 menu_entry(wxID_LOAD_LIBRARY
, towxstring(std::string("Load ") + library_is_called
));
732 menu_entry(wxID_RELOAD_ROM_IMAGE
, wxT("Reload ROM"));
733 menu_entry(wxID_LOAD_ROM_IMAGE
, wxT("ROM..."));
735 menu_start_sub(wxT("Save"));
736 menu_entry(wxID_SAVE_STATE
, wxT("State..."));
737 menu_entry(wxID_SAVE_MOVIE
, wxT("Movie..."));
738 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Screenshot..."));
739 menu_entry(wxID_SAVE_SUBTITLES
, wxT("Subtitles..."));
740 menu_entry(wxID_CANCEL_SAVES
, wxT("Cancel pending saves"));
743 menu_entry(wxID_EXIT
, wxT("Quit"));
745 menu_start(wxT("System"));
746 menu_entry(wxID_PAUSE
, wxT("Pause/Unpause"));
747 menu_entry(wxID_FRAMEADVANCE
, wxT("Step frame"));
748 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("Step subframe"));
749 menu_entry(wxID_NEXTPOLL
, wxT("Step poll"));
750 menu_entry(wxID_ERESET
, wxT("Reset"));
752 menu_start(wxT("Movie"));
753 menu_entry_check(wxID_READONLY_MODE
, wxT("Readonly mode"));
754 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
755 menu_entry(wxID_EDIT_AUTHORS
, wxT("Edit game name && authors..."));
756 menu_entry(wxID_EDIT_SUBTITLES
, wxT("Edit subtitles..."));
758 menu_entry(wxID_REWIND_MOVIE
, wxT("Rewind to start"));
760 //Autohold menu: (ACOS)
761 menu_special(wxT("Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
762 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
764 menu_start(wxT("Speed"));
765 menu_entry(wxID_SPEED_5
, wxT("1/20x"));
766 menu_entry(wxID_SPEED_10
, wxT("1/10x"));
767 menu_entry(wxID_SPEED_17
, wxT("1/6x"));
768 menu_entry(wxID_SPEED_20
, wxT("1/5x"));
769 menu_entry(wxID_SPEED_25
, wxT("1/4x"));
770 menu_entry(wxID_SPEED_33
, wxT("1/3x"));
771 menu_entry(wxID_SPEED_50
, wxT("1/2x"));
772 menu_entry(wxID_SPEED_100
, wxT("1x"));
773 menu_entry(wxID_SPEED_150
, wxT("1.5x"));
774 menu_entry(wxID_SPEED_200
, wxT("2x"));
775 menu_entry(wxID_SPEED_300
, wxT("3x"));
776 menu_entry(wxID_SPEED_500
, wxT("5x"));
777 menu_entry(wxID_SPEED_1000
, wxT("10x"));
778 menu_entry(wxID_SPEED_TURBO
, wxT("Turbo"));
779 menu_entry(wxID_SET_SPEED
, wxT("Set..."));
781 menu_start(wxT("Tools"));
782 menu_entry(wxID_RUN_SCRIPT
, wxT("Run batch file..."));
785 menu_entry(wxID_EVAL_LUA
, wxT("Evaluate Lua statement..."));
786 menu_entry(wxID_RUN_LUA
, wxT("Run Lua script..."));
788 menu_entry(wxID_RESET_LUA
, wxT("Reset Lua VM"));
791 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch..."));
793 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch..."));
794 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch..."));
796 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search..."));
798 menu_special_sub(wxT("Video Capture"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
799 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
801 menu_start(wxT("Configure"));
802 menu_entry_check(wxID_SHOW_STATUS
, wxT("Show/Hide status panel"));
803 menu_check(wxID_SHOW_STATUS
, true);
804 menu_entry(wxID_SHOW_MESSAGES
, wxT("Show messages"));
805 menu_entry(wxID_SETTINGS
, wxT("Configure emulator..."));
806 menu_entry(wxID_SETTINGS_HOTKEYS
, wxT("Configure hotkeys..."));
807 if(platform::sound_initialized()) {
809 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("Sounds enabled"));
810 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
811 menu_entry(wxID_SET_VOLUME
, wxT("Set Sound volume"));
812 menu_entry(wxID_SHOW_AUDIO_STATUS
, wxT("Show audio status"));
813 menu_special_sub(wxT("Select sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
814 new sound_select_menu(this)));
815 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
818 menu_start(wxT("Help"));
819 menu_entry(wxID_ABOUT
, wxT("About..."));
821 gpanel
->SetDropTarget(new loadfile());
822 spanel
->SetDropTarget(new loadfile());
825 void wxwin_mainwindow::request_paint()
830 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
832 //Veto it for now, latter things will delete it.
834 platform::queue("quit-emulator");
837 void wxwin_mainwindow::notify_update() throw()
839 if(!main_window_dirty
) {
840 main_window_dirty
= true;
845 void wxwin_mainwindow::notify_resized() throw()
848 toplevel
->SetSizeHints(this);
852 void wxwin_mainwindow::notify_update_status() throw()
855 spanel
->dirty
= true;
860 void wxwin_mainwindow::notify_exit() throw()
862 wxwidgets_exiting
= true;
863 join_emulator_thread();
867 #define NEW_KEYBINDING "A new binding..."
868 #define NEW_ALIAS "A new alias..."
869 #define NEW_WATCH "A new watch..."
871 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
874 handle_menu_click_cancelable(e
);
875 } catch(canceled_exception
& e
) {
877 } catch(std::bad_alloc
& e
) {
879 } catch(std::exception
& e
) {
880 show_message_ok(this, "Error in menu handler", e
.what(), wxICON_EXCLAMATION
);
884 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent
& e
)
886 std::string filename
;
889 case wxID_FRAMEADVANCE
:
890 platform::queue("+advance-frame");
891 platform::queue("-advance-frame");
893 case wxID_SUBFRAMEADVANCE
:
894 platform::queue("+advance-poll");
895 platform::queue("-advance-poll");
898 platform::queue("advance-skiplag");
901 platform::queue("pause-emulator");
904 platform::queue("reset");
907 platform::queue("quit-emulator");
909 case wxID_AUDIO_ENABLED
:
910 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
912 case wxID_SHOW_AUDIO_STATUS
:
913 platform::queue("show-sound-status");
915 case wxID_CANCEL_SAVES
:
916 platform::queue("cancel-saves");
918 case wxID_LOAD_MOVIE
:
919 platform::queue("load-movie " + pick_file(this, "Load Movie", movie_path(), false));
921 case wxID_LOAD_STATE
:
922 platform::queue("load " + pick_file(this, "Load State", movie_path(), false));
924 case wxID_LOAD_STATE_RO
:
925 platform::queue("load-readonly " + pick_file(this, "Load State (Read-Only)", movie_path(), false));
927 case wxID_LOAD_STATE_RW
:
928 platform::queue("load-state " + pick_file(this, "Load State (Read-Write)", movie_path(), false));
930 case wxID_LOAD_STATE_P
:
931 platform::queue("load-preserve " + pick_file(this, "Load State (Preserve)", movie_path(), false));
933 case wxID_REWIND_MOVIE
:
934 platform::queue("rewind-movie");
936 case wxID_SAVE_MOVIE
:
937 platform::queue("save-movie " + pick_file(this, "Save Movie", movie_path(), true));
939 case wxID_SAVE_SUBTITLES
:
940 platform::queue("save-subtitle " + pick_file(this, "Save Subtitle (.sub)", movie_path(), true));
942 case wxID_SAVE_STATE
:
943 platform::queue("save-state " + pick_file(this, "Save State", movie_path(), true));
945 case wxID_SAVE_SCREENSHOT
:
946 platform::queue("take-screenshot " + pick_file(this, "Save Screenshot", movie_path(), true));
948 case wxID_RUN_SCRIPT
:
949 platform::queue("run-script " + pick_file_member(this, "Select Script", "."));
952 platform::queue("run-lua " + pick_file(this, "Select Lua Script", ".", false));
955 platform::queue("reset-lua");
958 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
960 case wxID_READONLY_MODE
:
961 s
= menu_ischecked(wxID_READONLY_MODE
);
963 movb
.get_movie().readonly_mode(s
);
965 lua_callback_do_readwrite();
966 update_movie_state();
969 case wxID_EDIT_AUTHORS
:
970 wxeditor_authors_display(this);
972 case wxID_EDIT_SUBTITLES
:
973 wxeditor_subtitles_display(this);
975 case wxID_EDIT_MEMORYWATCH
:
976 wxeditor_memorywatch_display(this);
978 case wxID_SAVE_MEMORYWATCH
: {
979 modal_pause_holder hld
;
980 std::set
<std::string
> old_watches
;
981 runemufn([&old_watches
]() { old_watches
= get_watches(); });
982 std::string filename
= pick_file(this, "Save watches to file", ".", true);
983 std::ofstream
out(filename
.c_str());
984 for(auto i
: old_watches
) {
986 runemufn([i
, &val
]() { val
= get_watchexpr_for(i
); });
987 out
<< i
<< std::endl
<< val
<< std::endl
;
992 case wxID_LOAD_MEMORYWATCH
: {
993 modal_pause_holder hld
;
994 std::set
<std::string
> old_watches
;
995 runemufn([&old_watches
]() { old_watches
= get_watches(); });
996 std::map
<std::string
, std::string
> new_watches
;
997 std::string filename
= pick_file_member(this, "Choose memory watch file", ".");
1000 std::istream
& in
= open_file_relative(filename
, "");
1004 std::getline(in
, wname
);
1005 std::getline(in
, wexpr
);
1006 new_watches
[strip_CR(wname
)] = strip_CR(wexpr
);
1009 } catch(std::exception
& e
) {
1010 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e
.what(),
1011 wxICON_EXCLAMATION
);
1015 runemufn([&new_watches
, &old_watches
]() {
1016 for(auto i
: new_watches
)
1017 set_watchexpr_for(i
.first
, i
.second
);
1018 for(auto i
: old_watches
)
1019 if(!new_watches
.count(i
))
1020 set_watchexpr_for(i
, "");
1024 case wxID_MEMORY_SEARCH
:
1025 wxwindow_memorysearch_display();
1028 std::ostringstream str
;
1029 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1030 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1031 str
<< "Core: " << bsnes_core_version
<< std::endl
;
1032 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1035 case wxID_SHOW_STATUS
: {
1036 bool newstate
= menu_ischecked(wxID_SHOW_STATUS
);
1039 if(newstate
&& !spanel_shown
)
1040 toplevel
->Add(spanel
, 1, wxGROW
);
1041 else if(!newstate
&& spanel_shown
)
1042 toplevel
->Detach(spanel
);
1045 spanel_shown
= newstate
;
1047 toplevel
->SetSizeHints(this);
1051 case wxID_SET_SPEED
: {
1053 std::string value
= setting::is_set("targetfps") ? setting::get("targetfps") : "";
1054 value
= pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value
);
1056 setting::set("targetfps", value
);
1058 wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, this);
1062 case wxID_SET_VOLUME
: {
1066 value
= pick_text(this, "Set volume", "Enter volume in absolute units, percentage (%) or dB:",
1068 if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)", value
))
1069 parsed
= strtod(r
[1].c_str(), NULL
);
1070 else if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)%", value
))
1071 parsed
= strtod(r
[1].c_str(), NULL
) / 100;
1072 else if(r
= regex("([+-]?([0-9]*.[0-9]+|[0-9]+))dB", value
))
1073 parsed
= pow(10, strtod(r
[1].c_str(), NULL
) / 20);
1075 wxMessageBox(wxT("Invalid volume"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, this);
1078 last_volume
= value
;
1079 runemufn([parsed
]() { platform::global_volume
= parsed
; });
1089 set_speed(16.66666666666);
1098 set_speed(33.3333333333333);
1103 case wxID_SPEED_100
:
1106 case wxID_SPEED_150
:
1109 case wxID_SPEED_200
:
1112 case wxID_SPEED_300
:
1115 case wxID_SPEED_500
:
1118 case wxID_SPEED_1000
:
1121 case wxID_SPEED_TURBO
:
1124 case wxID_LOAD_LIBRARY
: {
1125 std::string name
= std::string("load ") + library_is_called
;
1126 load_library(pick_file(this, name
, ".", false));
1130 wxsetingsdialog_display(this, false);
1132 case wxID_SETTINGS_HOTKEYS
:
1133 wxsetingsdialog_display(this, true);
1135 case wxID_LOAD_ROM_IMAGE
:
1136 filename
= pick_file_member(this, "Select new ROM image", rom_path());
1137 platform::queue("unpause-emulator");
1138 platform::queue("reload-rom " + filename
);
1140 case wxID_RELOAD_ROM_IMAGE
:
1141 platform::queue("unpause-emulator");
1142 platform::queue("reload-rom");
1144 case wxID_NEW_MOVIE
:
1145 show_projectwindow(this);
1147 case wxID_SHOW_MESSAGES
:
1148 msg_window
->reshow();