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/lua.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/memorywatch.hpp"
12 #include "core/misc.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/window.hpp"
15 #include "core/zip.hpp"
20 #include "plat-wxwidgets/menu_dump.hpp"
21 #include "plat-wxwidgets/platform.hpp"
22 #include "plat-wxwidgets/window_mainwindow.hpp"
23 #include "plat-wxwidgets/window_status.hpp"
25 #define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
30 #define UINT64_C(val) val##ULL
32 #include <libswscale/swscale.h>
37 wxID_PAUSE
= wxID_HIGHEST
+ 1,
43 wxID_SHOW_AUDIO_STATUS
,
45 wxID_AUDIODEV_LAST
= wxID_AUDIODEV_FIRST
+ 255,
60 wxID_AUTOHOLD_LAST
= wxID_AUTOHOLD_FIRST
+ 127,
63 wxID_EDIT_KEYBINDINGS
,
65 wxID_EDIT_MEMORYWATCH
,
66 wxID_SAVE_MEMORYWATCH
,
67 wxID_LOAD_MEMORYWATCH
,
69 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
78 unsigned char* screen_buffer
;
81 bool main_window_dirty
;
82 struct thread
* emulation_thread
;
86 std::string windowname
= "lsnes rr" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
87 return towxstring(windowname
);
92 struct loaded_rom
* rom
;
93 struct moviefile
* initial
;
94 bool load_has_to_succeed
;
97 void* emulator_main(void* _args
)
99 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
102 struct moviefile
* movie
= args
->initial
;
103 bool has_to_succeed
= args
->load_has_to_succeed
;
104 platform::flush_command_queue();
105 main_loop(*our_rom
, *movie
, has_to_succeed
);
106 signal_program_exit();
107 } catch(std::bad_alloc
& e
) {
109 } catch(std::exception
& e
) {
110 messages
<< "FATAL: " << e
.what() << std::endl
;
111 platform::fatal_error();
116 void join_emulator_thread()
118 emulation_thread
->join();
121 void handle_wx_mouse(wxMouseEvent
& e
)
123 static uint32_t mask
= 0;
136 send_mouse_click(e
.GetX(), e
.GetY(), mask
);
139 bool is_readonly_mode()
142 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
146 bool UI_get_autohold(unsigned pid
, unsigned idx
)
149 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
153 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
155 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
158 int UI_controller_index_by_logical(unsigned lid
)
161 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
165 int UI_button_id(unsigned pcid
, unsigned lidx
)
168 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
172 class controller_autohold_menu
: public wxMenu
175 controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
);
178 void on_select(wxCommandEvent
& e
);
179 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
182 wxMenuItem
* entries
[MAX_LOGICAL_BUTTONS
];
183 unsigned enabled_entries
;
186 class autohold_menu
: public wxMenu
189 autohold_menu(wxwin_mainwindow
* win
);
191 void on_select(wxCommandEvent
& e
);
192 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
194 controller_autohold_menu
* menus
[MAXCONTROLLERS
];
195 wxMenuItem
* entries
[MAXCONTROLLERS
];
198 class sound_select_menu
: public wxMenu
201 sound_select_menu(wxwin_mainwindow
* win
);
202 void update(const std::string
& dev
);
203 void on_select(wxCommandEvent
& e
);
205 std::map
<std::string
, wxMenuItem
*> items
;
206 std::map
<int, std::string
> devices
;
209 class sound_select_menu
;
211 class broadcast_listener
: public information_dispatch
214 broadcast_listener(wxwin_mainwindow
* win
);
215 void set_sound_select(sound_select_menu
* sdev
);
216 void set_autohold_menu(autohold_menu
* ah
);
217 void on_sound_unmute(bool unmute
) throw();
218 void on_sound_change(const std::string
& dev
) throw();
219 void on_mode_change(bool readonly
) throw();
220 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
221 void on_autohold_reconfigure();
223 wxwin_mainwindow
* mainw
;
224 sound_select_menu
* sounddev
;
225 autohold_menu
* ahmenu
;
228 controller_autohold_menu::controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
)
230 platform::set_modal_pause(true);
232 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
233 int id
= wxID_AUTOHOLD_FIRST
+ MAX_LOGICAL_BUTTONS
* lid
+ i
;
234 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
237 platform::set_modal_pause(false);
240 void controller_autohold_menu::change_type()
243 int pid
= controls
.lcid_to_pcid(our_lid
);
244 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
247 pidx
= controls
.button_id(pid
, i
);
249 entries
[i
]->Check(pid
> 0 && UI_get_autohold(pid
, pidx
));
250 entries
[i
]->Enable();
253 entries
[i
]->Check(false);
254 entries
[i
]->Enable(false);
259 bool controller_autohold_menu::is_dummy()
261 return !enabled_entries
;
264 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
267 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* MAX_LOGICAL_BUTTONS
|| x
>= wxID_AUTOHOLD_FIRST
*
268 (our_lid
+ 1) * MAX_LOGICAL_BUTTONS
) {
271 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % MAX_LOGICAL_BUTTONS
;
272 platform::set_modal_pause(true);
273 int pid
= controls
.lcid_to_pcid(our_lid
);
274 if(pid
< 0 || !entries
[lidx
]) {
275 platform::set_modal_pause(false);
278 int pidx
= controls
.button_id(pid
, lidx
);
280 platform::set_modal_pause(false);
283 //Autohold change on pid=pid, ctrlindx=idx, state
284 bool newstate
= entries
[lidx
]->IsChecked();
285 UI_change_autohold(pid
, pidx
, newstate
);
286 platform::set_modal_pause(false);
289 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
291 platform::set_modal_pause(true);
292 int pid2
= UI_controller_index_by_logical(our_lid
);
293 if(pid2
< 0 || static_cast<unsigned>(pid
) != pid2
) {
294 platform::set_modal_pause(false);
297 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
298 int idx
= UI_button_id(pid2
, i
);
299 if(idx
< 0 || static_cast<unsigned>(idx
) != ctrlnum
)
301 entries
[i
]->Check(newstate
);
303 platform::set_modal_pause(false);
307 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
309 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
310 std::ostringstream str
;
311 str
<< "Controller #&" << (i
+ 1);
312 menus
[i
] = new controller_autohold_menu(i
, DT_NONE
);
313 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
314 entries
[i
]->Enable(!menus
[i
]->is_dummy());
316 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
317 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
321 void autohold_menu::reconfigure()
323 platform::set_modal_pause(true);
324 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
325 menus
[i
]->change_type();
326 entries
[i
]->Enable(!menus
[i
]->is_dummy());
328 platform::set_modal_pause(false);
331 void autohold_menu::on_select(wxCommandEvent
& e
)
333 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
334 menus
[i
]->on_select(e
);
337 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
339 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
340 menus
[i
]->update(pid
, ctrlnum
, newstate
);
343 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
345 std::string curdev
= platform::get_sound_device();
346 int j
= wxID_AUDIODEV_FIRST
;
347 for(auto i
: platform::get_sound_devices()) {
348 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
349 devices
[j
] = i
.first
;
350 if(i
.first
== curdev
)
351 items
[i
.first
]->Check();
352 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
353 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
358 void sound_select_menu::update(const std::string
& dev
)
363 void _do_sound_select(void* args)
365 std::string* x = reinterpret_cast<std::string*>(args);
366 platform::set_sound_device(*x);
369 void sound_select_menu::on_select(wxCommandEvent
& e
)
371 std::string devname
= devices
[e
.GetId()];
373 runemufn([devname
]() { platform::set_sound_device(devname
); });
376 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
377 : information_dispatch("wxwidgets-broadcast-listener")
382 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
387 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
392 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
394 runuifun([unmute
, mainw
]() { mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
397 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
399 runuifun([dev
, sounddev
]() { if(sounddev
) sounddev
->update(dev
); });
402 void broadcast_listener::on_mode_change(bool readonly
) throw()
404 runuifun([readonly
, mainw
]() { mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
407 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
409 runuifun([pid
, ctrlnum
, newstate
, ahmenu
]() { ahmenu
->update(pid
, ctrlnum
, newstate
); });
412 void broadcast_listener::on_autohold_reconfigure()
414 runuifun([ahmenu
]() { ahmenu
->reconfigure(); });
417 void _set_readonly(void* args
)
419 bool s
= *reinterpret_cast<bool*>(args
);
420 movb
.get_movie().readonly_mode(s
);
422 lua_callback_do_readwrite();
423 update_movie_state();
426 struct keyentry_mod_data
429 wxCheckBox
* unmasked
;
433 class wxdialog_keyentry
: public wxDialog
436 wxdialog_keyentry(wxWindow
* parent
);
437 void on_change_setting(wxCommandEvent
& e
);
438 void on_ok(wxCommandEvent
& e
);
439 void on_cancel(wxCommandEvent
& e
);
440 std::string
getkey();
442 std::map
<std::string
, keyentry_mod_data
> modifiers
;
448 wxdialog_keyentry::wxdialog_keyentry(wxWindow
* parent
)
449 : wxDialog(parent
, wxID_ANY
, wxT("Specify key"), wxDefaultPosition
, wxSize(-1, -1))
451 std::vector
<wxString
> keych
;
452 std::set
<std::string
> mods
, keys
;
454 runemufn([&mods
, &keys
]() { mods
= modifier::get_set(); keys
= keygroup::get_keys(); });
456 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
459 wxFlexGridSizer
* t_s
= new wxFlexGridSizer(mods
.size() + 1, 3, 0, 0);
461 t_s
->Add(new wxStaticText(this, wxID_ANY
, towxstring(i
)), 0, wxGROW
);
463 t_s
->Add(m
.pressed
= new wxCheckBox(this, wxID_ANY
, wxT("Pressed")), 0, wxGROW
);
464 t_s
->Add(m
.unmasked
= new wxCheckBox(this, wxID_ANY
, wxT("Unmasked")), 0, wxGROW
);
465 m
.pressed
->Disable();
467 m
.pressed
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
468 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
469 m
.unmasked
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
470 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
473 keych
.push_back(towxstring(i
));
474 t_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Key")), 0, wxGROW
);
475 t_s
->Add(mainkey
= new wxComboBox(this, wxID_ANY
, keych
[0], wxDefaultPosition
, wxDefaultSize
,
476 keych
.size(), &keych
[0], wxCB_READONLY
), 1, wxGROW
);
477 mainkey
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
478 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
481 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
482 pbutton_s
->AddStretchSpacer();
483 pbutton_s
->Add(ok
= new wxButton(this, wxID_OK
, wxT("OK")), 0, wxGROW
);
484 pbutton_s
->Add(cancel
= new wxButton(this, wxID_CANCEL
, wxT("Cancel")), 0, wxGROW
);
485 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
486 wxCommandEventHandler(wxdialog_keyentry::on_ok
), NULL
, this);
487 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
488 wxCommandEventHandler(wxdialog_keyentry::on_cancel
), NULL
, this);
489 top_s
->Add(pbutton_s
, 0, wxGROW
);
491 t_s
->SetSizeHints(this);
492 top_s
->SetSizeHints(this);
496 #define TMPFLAG_UNMASKED 65
497 #define TMPFLAG_UNMASKED_LINK_CHILD 2
498 #define TMPFLAG_UNMASKED_LINK_PARENT 68
499 #define TMPFLAG_PRESSED 8
500 #define TMPFLAG_PRESSED_LINK_CHILD 16
501 #define TMPFLAG_PRESSED_LINK_PARENT 32
503 void wxdialog_keyentry::on_change_setting(wxCommandEvent
& e
)
505 for(auto& i
: modifiers
)
506 i
.second
.tmpflags
= 0;
507 for(auto& i
: modifiers
) {
510 m
= &modifier::lookup(i
.first
);
512 i
.second
.pressed
->Disable();
513 i
.second
.unmasked
->Disable();
516 std::string j
= m
->linked_name();
517 if(i
.second
.unmasked
->GetValue())
518 i
.second
.tmpflags
|= TMPFLAG_UNMASKED
;
520 if(modifiers
[j
].unmasked
->GetValue())
521 i
.second
.tmpflags
|= TMPFLAG_UNMASKED_LINK_PARENT
;
522 if(i
.second
.unmasked
->GetValue())
523 modifiers
[j
].tmpflags
|= TMPFLAG_UNMASKED_LINK_CHILD
;
525 if(i
.second
.pressed
->GetValue())
526 i
.second
.tmpflags
|= TMPFLAG_PRESSED
;
528 if(modifiers
[j
].pressed
->GetValue())
529 i
.second
.tmpflags
|= TMPFLAG_PRESSED_LINK_PARENT
;
530 if(i
.second
.pressed
->GetValue())
531 modifiers
[j
].tmpflags
|= TMPFLAG_PRESSED_LINK_CHILD
;
534 for(auto& i
: modifiers
) {
535 //Unmasked is to be enabled if neither unmasked link flag is set.
536 if(i
.second
.tmpflags
& ((TMPFLAG_UNMASKED_LINK_CHILD
| TMPFLAG_UNMASKED_LINK_PARENT
) & ~64)) {
537 i
.second
.unmasked
->SetValue(false);
538 i
.second
.unmasked
->Disable();
540 i
.second
.unmasked
->Enable();
541 //Pressed is to be enabled if:
542 //- This modifier is unmasked or parent is unmasked.
543 //- Parent nor child is not pressed.
544 if(((i
.second
.tmpflags
& (TMPFLAG_UNMASKED
| TMPFLAG_UNMASKED_LINK_PARENT
|
545 TMPFLAG_PRESSED_LINK_CHILD
| TMPFLAG_PRESSED_LINK_PARENT
)) & 112) == 64)
546 i
.second
.pressed
->Enable();
548 i
.second
.pressed
->SetValue(false);
549 i
.second
.pressed
->Disable();
554 void wxdialog_keyentry::on_ok(wxCommandEvent
& e
)
559 void wxdialog_keyentry::on_cancel(wxCommandEvent
& e
)
561 EndModal(wxID_CANCEL
);
564 std::string
wxdialog_keyentry::getkey()
569 for(auto i
: modifiers
) {
570 if(i
.second
.pressed
->GetValue()) {
579 for(auto i
: modifiers
) {
580 if(i
.second
.unmasked
->GetValue()) {
587 x
= x
+ "|" + tostdstring(mainkey
->GetValue());
592 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
595 struct emu_args
* a
= new emu_args
;
598 a
->load_has_to_succeed
= false;
599 platform::set_modal_pause(true);
600 emulation_thread
= &thread::create(emulator_main
, a
);
601 main_window
= new wxwin_mainwindow();
603 platform::set_modal_pause(false);
604 } catch(std::bad_alloc
& e
) {
609 wxwin_mainwindow::panel::panel(wxWindow
* win
)
612 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
613 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
614 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
615 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
616 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
617 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
618 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
619 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
620 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
621 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
622 SetMinSize(wxSize(512, 448));
625 void wxwin_mainwindow::menu_start(wxString name
)
627 while(!upper
.empty())
629 current_menu
= new wxMenu();
630 menubar
->Append(current_menu
, name
);
633 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
635 while(!upper
.empty())
637 menubar
->Append(menu
, name
);
641 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
643 current_menu
->AppendSubMenu(menu
, name
);
646 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
648 current_menu
->Append(id
, name
);
649 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
650 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
653 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
655 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
656 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
657 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
660 void wxwin_mainwindow::menu_start_sub(wxString name
)
662 wxMenu
* old
= current_menu
;
663 upper
.push(current_menu
);
664 current_menu
= new wxMenu();
665 old
->AppendSubMenu(current_menu
, name
);
668 void wxwin_mainwindow::menu_end_sub(wxString name
)
670 current_menu
= upper
.top();
674 bool wxwin_mainwindow::menu_ischecked(int id
)
676 if(checkitems
.count(id
))
677 return checkitems
[id
]->IsChecked();
682 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
684 if(checkitems
.count(id
))
685 return checkitems
[id
]->Check(newstate
);
690 void wxwin_mainwindow::menu_separator()
692 current_menu
->AppendSeparator();
695 void wxwin_mainwindow::panel::request_paint()
700 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
702 render_framebuffer();
703 static struct SwsContext
* ctx
;
709 if(!screen_buffer
|| main_screen
.width
!= old_width
|| main_screen
.height
!= old_height
) {
711 delete[] screen_buffer
;
712 screen_buffer
= new unsigned char[main_screen
.width
* main_screen
.height
* 3];
713 old_height
= main_screen
.height
;
714 old_width
= main_screen
.width
;
715 uint32_t w
= main_screen
.width
;
716 uint32_t h
= main_screen
.height
;
718 ctx
= sws_getCachedContext(ctx
, w
, h
, PIX_FMT_RGBA
, w
, h
, PIX_FMT_BGR24
, SWS_POINT
|
719 SWS_CPU_CAPS_MMX2
, NULL
, NULL
, NULL
);
724 SetMinSize(wxSize(w
, h
));
727 srcs
[0] = 4 * main_screen
.width
;
728 dsts
[0] = 3 * main_screen
.width
;
729 srcp
[0] = reinterpret_cast<unsigned char*>(main_screen
.memory
);
730 dstp
[0] = screen_buffer
;
731 memset(screen_buffer
, 0, main_screen
.width
* main_screen
.height
* 3);
732 uint64_t t1
= get_utime();
733 if(main_screen
.width
&& main_screen
.height
)
734 sws_scale(ctx
, srcp
, srcs
, 0, main_screen
.height
, dstp
, dsts
);
735 uint64_t t2
= get_utime();
736 wxBitmap
bmp(wxImage(main_screen
.width
, main_screen
.height
, screen_buffer
, true));
737 uint64_t t3
= get_utime();
738 dc
.DrawBitmap(bmp
, 0, 0, false);
739 main_window_dirty
= false;
742 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
747 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
749 handle_wx_keyboard(e
, true);
752 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
754 handle_wx_keyboard(e
, false);
757 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
762 wxwin_mainwindow::wxwin_mainwindow()
763 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
764 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
766 broadcast_listener
* blistener
= new broadcast_listener(this);
768 wxFlexGridSizer
* toplevel
= new wxFlexGridSizer(1, 1, 0, 0);
769 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
770 toplevel
->SetSizeHints(this);
774 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
775 menubar
= new wxMenuBar
;
778 //TOP-level accels: ACFOS.
779 //System menu: (ACFOS)EMNPQRU
780 menu_start(wxT("&System"));
781 menu_entry(wxID_FRAMEADVANCE
, wxT("Fra&me advance"));
782 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("S&ubframe advance"));
783 menu_entry(wxID_NEXTPOLL
, wxT("&Next poll"));
784 menu_entry(wxID_PAUSE
, wxT("&Pause/Unpause"));
786 menu_entry(wxID_ERESET
, wxT("&Reset"));
788 menu_entry(wxID_EDIT_AUTHORS
, wxT("&Edit game name && authors"));
790 menu_entry(wxID_EXIT
, wxT("&Quit"));
792 menu_entry(wxID_ABOUT
, wxT("About"));
793 //File menu: (ACFOS)DELMNPRTUVW
794 menu_start(wxT("&File"));
795 menu_entry_check(wxID_READONLY_MODE
, wxT("Reado&nly mode"));
796 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
798 menu_entry(wxID_SAVE_STATE
, wxT("Save stat&e"));
799 menu_entry(wxID_SAVE_MOVIE
, wxT("Sa&ve movie"));
801 menu_entry(wxID_LOAD_STATE
, wxT("&Load state"));
802 menu_entry(wxID_LOAD_STATE_RO
, wxT("Loa&d state (readonly)"));
803 menu_entry(wxID_LOAD_STATE_RW
, wxT("Load s&tate (read-write)"));
804 menu_entry(wxID_LOAD_STATE_P
, wxT("Load state (&preserve)"));
805 menu_entry(wxID_LOAD_MOVIE
, wxT("Load &movie"));
806 menu_entry(wxID_REWIND_MOVIE
, wxT("Re&wind movie"));
808 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Save sc&reenshot"));
810 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
811 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
812 //Autohold menu: (ACFOS)
813 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
814 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
815 //Scripting menu: (ACFOS)ERU
816 menu_start(wxT("S&cripting"));
817 menu_entry(wxID_RUN_SCRIPT
, wxT("&Run script"));
820 menu_entry(wxID_EVAL_LUA
, wxT("&Evaluate Lua statement"));
821 menu_entry(wxID_RUN_LUA
, wxT("R&un Lua script"));
824 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch"));
826 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch"));
827 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch"));
829 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search"));
830 //Settings menu: (ACFOS)
831 menu_start(wxT("Settings"));
832 menu_entry(wxID_EDIT_AXES
, wxT("Configure axes"));
833 menu_entry(wxID_EDIT_SETTINGS
, wxT("Configure settings"));
834 menu_entry(wxID_EDIT_KEYBINDINGS
, wxT("Configure keybindings"));
835 menu_entry(wxID_EDIT_ALIAS
, wxT("Configure aliases"));
836 menu_entry(wxID_EDIT_JUKEBOX
, wxT("Configure jukebox"));
837 if(platform::sound_initialized()) {
838 //Sound menu: (ACFOS)EHU
839 menu_start(wxT("S&ound"));
840 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("So&unds enabled"));
841 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
842 menu_entry(wxID_SHOW_AUDIO_STATUS
, wxT("S&how audio status"));
844 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
845 new sound_select_menu(this)));
846 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
850 void wxwin_mainwindow::request_paint()
855 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
857 //Veto it for now, latter things will delete it.
859 platform::queue("quit-emulator");
862 void wxwin_mainwindow::notify_update() throw()
864 if(!main_window_dirty
) {
865 main_window_dirty
= true;
870 void wxwin_mainwindow::notify_exit() throw()
872 join_emulator_thread();
876 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
879 wxTextEntryDialog
* d2
;
880 std::string filename
;
883 case wxID_FRAMEADVANCE
:
884 platform::queue("+advance-frame");
885 platform::queue("-advance-frame");
887 case wxID_SUBFRAMEADVANCE
:
888 platform::queue("+advance-poll");
889 platform::queue("-advance-poll");
892 platform::queue("advance-skiplag");
895 platform::queue("pause-emulator");
898 platform::queue("reset");
901 platform::queue("quit-emulator");
903 case wxID_AUDIO_ENABLED
:
904 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
906 case wxID_SHOW_AUDIO_STATUS
:
907 platform::queue("show-sound-status");
909 case wxID_LOAD_MOVIE
:
910 d
= new wxFileDialog(this, wxT("Load Movie"), wxT("."));
911 if(d
->ShowModal() == wxID_OK
)
912 filename
= tostdstring(d
->GetPath());
916 platform::queue("load-movie " + filename
);
918 case wxID_LOAD_STATE
:
919 d
= new wxFileDialog(this, wxT("Load State"), wxT("."));
920 if(d
->ShowModal() == wxID_OK
)
921 filename
= tostdstring(d
->GetPath());
925 platform::queue("load " + filename
);
927 case wxID_LOAD_STATE_RO
:
928 d
= new wxFileDialog(this, wxT("Load State (Read-Only)"), wxT("."));
929 if(d
->ShowModal() == wxID_OK
)
930 filename
= tostdstring(d
->GetPath());
934 platform::queue("load-readonly " + filename
);
936 case wxID_LOAD_STATE_RW
:
937 d
= new wxFileDialog(this, wxT("Load State (Read-Write)"), wxT("."));
938 if(d
->ShowModal() == wxID_OK
)
939 filename
= tostdstring(d
->GetPath());
943 platform::queue("load-state " + filename
);
945 case wxID_LOAD_STATE_P
:
946 d
= new wxFileDialog(this, wxT("Load State (Preserve)"), wxT("."));
947 if(d
->ShowModal() == wxID_OK
)
948 filename
= tostdstring(d
->GetPath());
952 platform::queue("load-preserve " + filename
);
954 case wxID_REWIND_MOVIE
:
955 platform::queue("rewind-movie");
957 case wxID_SAVE_MOVIE
:
958 d
= new wxFileDialog(this, wxT("Save Movie"), wxT("."));
959 if(d
->ShowModal() == wxID_OK
)
960 filename
= tostdstring(d
->GetPath());
964 platform::queue("save-movie " + filename
);
966 case wxID_SAVE_STATE
:
967 d
= new wxFileDialog(this, wxT("Save State"), wxT("."));
968 if(d
->ShowModal() == wxID_OK
)
969 filename
= tostdstring(d
->GetPath());
973 platform::queue("save-state " + filename
);
975 case wxID_SAVE_SCREENSHOT
:
976 d
= new wxFileDialog(this, wxT("Save Screenshot"), wxT("."));
977 if(d
->ShowModal() == wxID_OK
)
978 filename
= tostdstring(d
->GetPath());
982 platform::queue("take-screenshot " + filename
);
984 case wxID_RUN_SCRIPT
:
985 d
= new wxFileDialog(this, wxT("Select Script"), wxT("."));
986 if(d
->ShowModal() == wxID_OK
)
987 filename
= tostdstring(d
->GetPath());
991 platform::queue("run-script " + filename
);
994 d
= new wxFileDialog(this, wxT("Select Lua Script"), wxT("."));
995 if(d
->ShowModal() == wxID_OK
)
996 filename
= tostdstring(d
->GetPath());
1000 platform::queue("run-lua " + filename
);
1003 d2
= new wxTextEntryDialog(this, wxT("Enter Lua statement:"), wxT("Evaluate Lua"));
1004 if(d2
->ShowModal() == wxID_OK
)
1005 filename
= tostdstring(d2
->GetValue());
1007 platform::queue("evaluate-lua " + filename
);
1009 case wxID_READONLY_MODE
:
1010 s
= menu_ischecked(wxID_READONLY_MODE
);
1011 platform::queue(_set_readonly
, &s
, true);
1013 case wxID_EDIT_AXES
:
1014 wxeditor_axes_display(this);
1016 case wxID_EDIT_AUTHORS
:
1017 wxeditor_authors_display(this);
1019 case wxID_EDIT_SETTINGS
:
1020 wxeditor_settings_display(this);
1022 case wxID_EDIT_KEYBINDINGS
:
1023 menu_edit_keybindings(e
);
1025 case wxID_EDIT_ALIAS
:
1026 menu_edit_aliases(e
);
1028 case wxID_EDIT_JUKEBOX
:
1029 menu_edit_jukebox(e
);
1031 case wxID_EDIT_MEMORYWATCH
:
1032 menu_edit_memorywatch(e
);
1034 case wxID_SAVE_MEMORYWATCH
:
1035 menu_save_memorywatch(e
);
1037 case wxID_LOAD_MEMORYWATCH
:
1038 menu_load_memorywatch(e
);
1040 case wxID_MEMORY_SEARCH
:
1041 wxwindow_memorysearch_display();
1044 std::ostringstream str
;
1045 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1046 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1047 str
<< "Core: " << bsnes_core_version
<< std::endl
;
1048 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1054 #define NEW_KEYBINDING "A new binding..."
1055 #define NEW_ALIAS "A new alias..."
1056 #define NEW_WATCH "A new watch..."
1058 void wxwin_mainwindow::menu_edit_keybindings(wxCommandEvent
& e
)
1060 platform::set_modal_pause(true);
1061 std::set
<std::string
> bind
;
1062 runemufn([&bind
]() { bind
= keymapper::get_bindings(); });
1063 std::vector
<wxString
> choices
;
1064 choices
.push_back(wxT(NEW_KEYBINDING
));
1066 choices
.push_back(towxstring(i
));
1067 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1068 wxT("Select binding"), choices
.size(), &choices
[0]);
1069 if(d
->ShowModal() == wxID_CANCEL
) {
1071 platform::set_modal_pause(false);
1074 std::string key
= tostdstring(d
->GetStringSelection());
1076 if(key
== NEW_KEYBINDING
) {
1077 wxdialog_keyentry
* d2
= new wxdialog_keyentry(this);
1078 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1079 // wxT("Edit binding"), wxT(""));
1080 if(d2
->ShowModal() == wxID_CANCEL
) {
1082 platform::set_modal_pause(false);
1086 //key = tostdstring(d2->GetValue());
1089 std::string old_command_value
;
1090 runemufn([&old_command_value
, key
]() { old_command_value
= keymapper::get_command_for(key
); });
1091 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1092 towxstring(old_command_value
));
1093 if(d4
->ShowModal() == wxID_CANCEL
) {
1095 platform::set_modal_pause(false);
1099 std::string faulttext
;
1100 std::string newcommand
= tostdstring(d4
->GetValue());
1101 runemufn([&fault
, &faulttext
, key
, newcommand
]() {
1103 keymapper::bind_for(key
, newcommand
);
1104 } catch(std::exception
& e
) {
1108 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1109 faulttext
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1114 platform::set_modal_pause(false);
1117 void strip_CR(std::string
& x
) throw(std::bad_alloc
);
1119 void wxwin_mainwindow::menu_edit_aliases(wxCommandEvent
& e
)
1121 platform::set_modal_pause(true);
1122 std::set
<std::string
> bind
;
1123 runemufn([&bind
]() { bind
= command::get_aliases(); });
1124 std::vector
<wxString
> choices
;
1125 choices
.push_back(wxT(NEW_ALIAS
));
1127 choices
.push_back(towxstring(i
));
1128 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1129 wxT("Select alias"), choices
.size(), &choices
[0]);
1130 if(d
->ShowModal() == wxID_CANCEL
) {
1132 platform::set_modal_pause(false);
1135 std::string alias
= tostdstring(d
->GetStringSelection());
1137 if(alias
== NEW_ALIAS
) {
1138 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1139 wxT("Enter alias name"));
1140 if(d2
->ShowModal() == wxID_CANCEL
) {
1142 platform::set_modal_pause(false);
1145 alias
= tostdstring(d2
->GetValue());
1147 if(!command::valid_alias_name(alias
)) {
1148 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1149 "name: ") + alias
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1152 platform::set_modal_pause(false);
1156 std::string old_alias_value
= command::get_alias_for(alias
);
1157 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1158 towxstring(old_alias_value
), wxOK
| wxCANCEL
| wxCENTRE
| wxTE_MULTILINE
);
1159 if(d4
->ShowModal() == wxID_CANCEL
) {
1161 platform::set_modal_pause(false);
1164 std::string newcmd
= tostdstring(d4
->GetValue());
1165 runemufn([alias
, newcmd
]() { command::set_alias_for(alias
, newcmd
); });
1167 platform::set_modal_pause(false);
1170 void wxwin_mainwindow::menu_edit_jukebox(wxCommandEvent
& e
)
1172 platform::set_modal_pause(true);
1174 std::vector
<std::string
> new_jukebox
;
1176 for(auto i
: get_jukebox_names())
1180 wxTextEntryDialog
* dialog
= new wxTextEntryDialog(this, wxT("List jukebox entries"), wxT("Configure jukebox"),
1181 towxstring(x
), wxOK
| wxCANCEL
| wxCENTRE
| wxTE_MULTILINE
);
1182 if(dialog
->ShowModal() == wxID_CANCEL
) {
1184 platform::set_modal_pause(false);
1187 x
= tostdstring(dialog
->GetValue());
1191 size_t split
= x
.find_first_of("\n");
1193 if(split
< x
.length()) {
1194 l
= x
.substr(0, split
);
1195 x
= x
.substr(split
+ 1);
1202 new_jukebox
.push_back(l
);
1204 runemufn([&new_jukebox
]() { set_jukebox_names(new_jukebox
); });
1205 status_window
->notify_update();
1206 platform::set_modal_pause(false);
1209 void wxwin_mainwindow::menu_load_memorywatch(wxCommandEvent
& e
)
1211 platform::set_modal_pause(true);
1212 std::set
<std::string
> old_watches
;
1213 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1214 std::map
<std::string
, std::string
> new_watches
;
1215 std::string filename
;
1217 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1218 if(d
->ShowModal() == wxID_CANCEL
) {
1220 platform::set_modal_pause(false);
1223 filename
= tostdstring(d
->GetPath());
1225 //Did we pick a .zip file?
1227 zip_reader
zr(filename
);
1228 std::vector
<wxString
> files
;
1230 files
.push_back(towxstring(i
));
1231 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1232 wxT("Select member"), files
.size(), &files
[0]);
1233 if(d2
->ShowModal() == wxID_CANCEL
) {
1235 platform::set_modal_pause(false);
1238 filename
= filename
+ "/" + tostdstring(d2
->GetStringSelection());
1245 std::istream
& in
= open_file_relative(filename
, "");
1249 std::getline(in
, wname
);
1250 std::getline(in
, wexpr
);
1251 new_watches
[wname
] = wexpr
;
1254 } catch(std::exception
& e
) {
1255 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1256 "watch: ") + e
.what()), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1259 platform::set_modal_pause(false);
1263 runemufn([&new_watches
, &old_watches
]() {
1264 for(auto i
: new_watches
)
1265 set_watchexpr_for(i
.first
, i
.second
);
1266 for(auto i
: old_watches
)
1267 if(!new_watches
.count(i
))
1268 set_watchexpr_for(i
, "");
1270 platform::set_modal_pause(false);
1273 void wxwin_mainwindow::menu_save_memorywatch(wxCommandEvent
& e
)
1275 platform::set_modal_pause(true);
1276 std::set
<std::string
> old_watches
;
1277 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1278 std::string filename
;
1280 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1281 if(d
->ShowModal() == wxID_CANCEL
) {
1283 platform::set_modal_pause(false);
1286 filename
= tostdstring(d
->GetPath());
1289 std::ofstream
out(filename
.c_str());
1290 for(auto i
: old_watches
)
1291 out
<< i
<< std::endl
<< get_watchexpr_for(i
) << std::endl
;
1293 platform::set_modal_pause(false);
1297 void wxwin_mainwindow::menu_edit_memorywatch(wxCommandEvent
& e
)
1299 platform::set_modal_pause(true);
1300 std::set
<std::string
> bind
;
1301 runemufn([&bind
]() { bind
= get_watches(); });
1302 std::vector
<wxString
> choices
;
1303 choices
.push_back(wxT(NEW_WATCH
));
1305 choices
.push_back(towxstring(i
));
1306 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1307 wxT("Select watch"), choices
.size(), &choices
[0]);
1308 if(d
->ShowModal() == wxID_CANCEL
) {
1310 platform::set_modal_pause(false);
1313 std::string watch
= tostdstring(d
->GetStringSelection());
1315 if(watch
== NEW_WATCH
) {
1316 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1317 wxT("Enter watch name"));
1318 if(d2
->ShowModal() == wxID_CANCEL
) {
1320 platform::set_modal_pause(false);
1323 watch
= tostdstring(d2
->GetValue());
1326 std::string old_watch_value
= get_watchexpr_for(watch
);
1327 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1328 towxstring(old_watch_value
), wxOK
| wxCANCEL
| wxCENTRE
);
1329 if(d4
->ShowModal() == wxID_CANCEL
) {
1331 platform::set_modal_pause(false);
1334 std::string newexpr
= tostdstring(d4
->GetValue());
1335 runemufn([watch
, newexpr
]() { set_watchexpr_for(watch
, newexpr
); });
1336 platform::set_modal_pause(false);