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"
24 #define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
29 #define UINT64_C(val) val##ULL
31 #include <libswscale/swscale.h>
36 wxID_PAUSE
= wxID_HIGHEST
+ 1,
42 wxID_SHOW_AUDIO_STATUS
,
44 wxID_AUDIODEV_LAST
= wxID_AUDIODEV_FIRST
+ 255,
59 wxID_AUTOHOLD_LAST
= wxID_AUTOHOLD_FIRST
+ 127,
62 wxID_EDIT_KEYBINDINGS
,
64 wxID_EDIT_MEMORYWATCH
,
65 wxID_SAVE_MEMORYWATCH
,
66 wxID_LOAD_MEMORYWATCH
,
68 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
75 unsigned char* screen_buffer
;
78 bool main_window_dirty
;
79 struct thread
* emulation_thread
;
83 std::string windowname
= "lsnes rr" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
84 return towxstring(windowname
);
89 struct loaded_rom
* rom
;
90 struct moviefile
* initial
;
91 bool load_has_to_succeed
;
94 void* emulator_main(void* _args
)
96 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
99 struct moviefile
* movie
= args
->initial
;
100 bool has_to_succeed
= args
->load_has_to_succeed
;
101 platform::flush_command_queue();
102 main_loop(*our_rom
, *movie
, has_to_succeed
);
103 signal_program_exit();
104 } catch(std::bad_alloc
& e
) {
106 } catch(std::exception
& e
) {
107 messages
<< "FATAL: " << e
.what() << std::endl
;
108 platform::fatal_error();
113 void join_emulator_thread()
115 emulation_thread
->join();
118 void handle_wx_mouse(wxMouseEvent
& e
)
120 static uint32_t mask
= 0;
133 send_mouse_click(e
.GetX(), e
.GetY(), mask
);
136 bool is_readonly_mode()
139 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
143 bool UI_get_autohold(unsigned pid
, unsigned idx
)
146 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
150 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
152 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
155 int UI_controller_index_by_logical(unsigned lid
)
158 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
162 int UI_button_id(unsigned pcid
, unsigned lidx
)
165 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
169 class controller_autohold_menu
: public wxMenu
172 controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
);
175 void on_select(wxCommandEvent
& e
);
176 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
179 wxMenuItem
* entries
[MAX_LOGICAL_BUTTONS
];
180 unsigned enabled_entries
;
183 class autohold_menu
: public wxMenu
186 autohold_menu(wxwin_mainwindow
* win
);
188 void on_select(wxCommandEvent
& e
);
189 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
191 controller_autohold_menu
* menus
[MAXCONTROLLERS
];
192 wxMenuItem
* entries
[MAXCONTROLLERS
];
195 class sound_select_menu
: public wxMenu
198 sound_select_menu(wxwin_mainwindow
* win
);
199 void update(const std::string
& dev
);
200 void on_select(wxCommandEvent
& e
);
202 std::map
<std::string
, wxMenuItem
*> items
;
203 std::map
<int, std::string
> devices
;
206 class sound_select_menu
;
208 class broadcast_listener
: public information_dispatch
211 broadcast_listener(wxwin_mainwindow
* win
);
212 void set_sound_select(sound_select_menu
* sdev
);
213 void set_autohold_menu(autohold_menu
* ah
);
214 void on_sound_unmute(bool unmute
) throw();
215 void on_sound_change(const std::string
& dev
) throw();
216 void on_mode_change(bool readonly
) throw();
217 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
218 void on_autohold_reconfigure();
220 wxwin_mainwindow
* mainw
;
221 sound_select_menu
* sounddev
;
222 autohold_menu
* ahmenu
;
225 controller_autohold_menu::controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
)
227 platform::set_modal_pause(true);
229 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
230 int id
= wxID_AUTOHOLD_FIRST
+ MAX_LOGICAL_BUTTONS
* lid
+ i
;
231 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
234 platform::set_modal_pause(false);
237 void controller_autohold_menu::change_type()
240 int pid
= controls
.lcid_to_pcid(our_lid
);
241 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
244 pidx
= controls
.button_id(pid
, i
);
246 entries
[i
]->Check(pid
> 0 && UI_get_autohold(pid
, pidx
));
247 entries
[i
]->Enable();
250 entries
[i
]->Check(false);
251 entries
[i
]->Enable(false);
256 bool controller_autohold_menu::is_dummy()
258 return !enabled_entries
;
261 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
264 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* MAX_LOGICAL_BUTTONS
|| x
>= wxID_AUTOHOLD_FIRST
*
265 (our_lid
+ 1) * MAX_LOGICAL_BUTTONS
) {
268 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % MAX_LOGICAL_BUTTONS
;
269 platform::set_modal_pause(true);
270 int pid
= controls
.lcid_to_pcid(our_lid
);
271 if(pid
< 0 || !entries
[lidx
]) {
272 platform::set_modal_pause(false);
275 int pidx
= controls
.button_id(pid
, lidx
);
277 platform::set_modal_pause(false);
280 //Autohold change on pid=pid, ctrlindx=idx, state
281 bool newstate
= entries
[lidx
]->IsChecked();
282 UI_change_autohold(pid
, pidx
, newstate
);
283 platform::set_modal_pause(false);
286 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
288 platform::set_modal_pause(true);
289 int pid2
= UI_controller_index_by_logical(our_lid
);
290 if(pid2
< 0 || static_cast<unsigned>(pid
) != pid2
) {
291 platform::set_modal_pause(false);
294 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
295 int idx
= UI_button_id(pid2
, i
);
296 if(idx
< 0 || static_cast<unsigned>(idx
) != ctrlnum
)
298 entries
[i
]->Check(newstate
);
300 platform::set_modal_pause(false);
304 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
306 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
307 std::ostringstream str
;
308 str
<< "Controller #&" << (i
+ 1);
309 menus
[i
] = new controller_autohold_menu(i
, DT_NONE
);
310 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
311 entries
[i
]->Enable(!menus
[i
]->is_dummy());
313 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
314 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
318 void autohold_menu::reconfigure()
320 platform::set_modal_pause(true);
321 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
322 menus
[i
]->change_type();
323 entries
[i
]->Enable(!menus
[i
]->is_dummy());
325 platform::set_modal_pause(false);
328 void autohold_menu::on_select(wxCommandEvent
& e
)
330 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
331 menus
[i
]->on_select(e
);
334 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
336 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
337 menus
[i
]->update(pid
, ctrlnum
, newstate
);
340 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
342 std::string curdev
= platform::get_sound_device();
343 int j
= wxID_AUDIODEV_FIRST
;
344 for(auto i
: platform::get_sound_devices()) {
345 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
346 devices
[j
] = i
.first
;
347 if(i
.first
== curdev
)
348 items
[i
.first
]->Check();
349 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
350 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
355 void sound_select_menu::update(const std::string
& dev
)
360 void _do_sound_select(void* args)
362 std::string* x = reinterpret_cast<std::string*>(args);
363 platform::set_sound_device(*x);
366 void sound_select_menu::on_select(wxCommandEvent
& e
)
368 std::string devname
= devices
[e
.GetId()];
370 runemufn([devname
]() { platform::set_sound_device(devname
); });
373 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
374 : information_dispatch("wxwidgets-broadcast-listener")
379 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
384 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
389 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
391 runuifun([unmute
, mainw
]() { mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
394 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
396 runuifun([dev
, sounddev
]() { if(sounddev
) sounddev
->update(dev
); });
399 void broadcast_listener::on_mode_change(bool readonly
) throw()
401 runuifun([readonly
, mainw
]() { mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
404 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
406 runuifun([pid
, ctrlnum
, newstate
, ahmenu
]() { ahmenu
->update(pid
, ctrlnum
, newstate
); });
409 void broadcast_listener::on_autohold_reconfigure()
411 runuifun([ahmenu
]() { ahmenu
->reconfigure(); });
414 void _set_readonly(void* args
)
416 bool s
= *reinterpret_cast<bool*>(args
);
417 movb
.get_movie().readonly_mode(s
);
419 lua_callback_do_readwrite();
420 update_movie_state();
423 struct keyentry_mod_data
426 wxCheckBox
* unmasked
;
430 class wxdialog_keyentry
: public wxDialog
433 wxdialog_keyentry(wxWindow
* parent
);
434 void on_change_setting(wxCommandEvent
& e
);
435 void on_ok(wxCommandEvent
& e
);
436 void on_cancel(wxCommandEvent
& e
);
437 std::string
getkey();
439 std::map
<std::string
, keyentry_mod_data
> modifiers
;
445 wxdialog_keyentry::wxdialog_keyentry(wxWindow
* parent
)
446 : wxDialog(parent
, wxID_ANY
, wxT("Specify key"), wxDefaultPosition
, wxSize(-1, -1))
448 std::vector
<wxString
> keych
;
449 std::set
<std::string
> mods
, keys
;
451 runemufn([&mods
, &keys
]() { mods
= modifier::get_set(); keys
= keygroup::get_keys(); });
453 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
456 wxFlexGridSizer
* t_s
= new wxFlexGridSizer(mods
.size() + 1, 3, 0, 0);
458 t_s
->Add(new wxStaticText(this, wxID_ANY
, towxstring(i
)), 0, wxGROW
);
460 t_s
->Add(m
.pressed
= new wxCheckBox(this, wxID_ANY
, wxT("Pressed")), 0, wxGROW
);
461 t_s
->Add(m
.unmasked
= new wxCheckBox(this, wxID_ANY
, wxT("Unmasked")), 0, wxGROW
);
462 m
.pressed
->Disable();
464 m
.pressed
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
465 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
466 m
.unmasked
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
467 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
470 keych
.push_back(towxstring(i
));
471 t_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Key")), 0, wxGROW
);
472 t_s
->Add(mainkey
= new wxComboBox(this, wxID_ANY
, keych
[0], wxDefaultPosition
, wxDefaultSize
,
473 keych
.size(), &keych
[0], wxCB_READONLY
), 1, wxGROW
);
474 mainkey
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
475 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
478 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
479 pbutton_s
->AddStretchSpacer();
480 pbutton_s
->Add(ok
= new wxButton(this, wxID_OK
, wxT("OK")), 0, wxGROW
);
481 pbutton_s
->Add(cancel
= new wxButton(this, wxID_CANCEL
, wxT("Cancel")), 0, wxGROW
);
482 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
483 wxCommandEventHandler(wxdialog_keyentry::on_ok
), NULL
, this);
484 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
485 wxCommandEventHandler(wxdialog_keyentry::on_cancel
), NULL
, this);
486 top_s
->Add(pbutton_s
, 0, wxGROW
);
488 t_s
->SetSizeHints(this);
489 top_s
->SetSizeHints(this);
493 #define TMPFLAG_UNMASKED 65
494 #define TMPFLAG_UNMASKED_LINK_CHILD 2
495 #define TMPFLAG_UNMASKED_LINK_PARENT 68
496 #define TMPFLAG_PRESSED 8
497 #define TMPFLAG_PRESSED_LINK_CHILD 16
498 #define TMPFLAG_PRESSED_LINK_PARENT 32
500 void wxdialog_keyentry::on_change_setting(wxCommandEvent
& e
)
502 for(auto& i
: modifiers
)
503 i
.second
.tmpflags
= 0;
504 for(auto& i
: modifiers
) {
507 m
= &modifier::lookup(i
.first
);
509 i
.second
.pressed
->Disable();
510 i
.second
.unmasked
->Disable();
513 std::string j
= m
->linked_name();
514 if(i
.second
.unmasked
->GetValue())
515 i
.second
.tmpflags
|= TMPFLAG_UNMASKED
;
517 if(modifiers
[j
].unmasked
->GetValue())
518 i
.second
.tmpflags
|= TMPFLAG_UNMASKED_LINK_PARENT
;
519 if(i
.second
.unmasked
->GetValue())
520 modifiers
[j
].tmpflags
|= TMPFLAG_UNMASKED_LINK_CHILD
;
522 if(i
.second
.pressed
->GetValue())
523 i
.second
.tmpflags
|= TMPFLAG_PRESSED
;
525 if(modifiers
[j
].pressed
->GetValue())
526 i
.second
.tmpflags
|= TMPFLAG_PRESSED_LINK_PARENT
;
527 if(i
.second
.pressed
->GetValue())
528 modifiers
[j
].tmpflags
|= TMPFLAG_PRESSED_LINK_CHILD
;
531 for(auto& i
: modifiers
) {
532 //Unmasked is to be enabled if neither unmasked link flag is set.
533 if(i
.second
.tmpflags
& ((TMPFLAG_UNMASKED_LINK_CHILD
| TMPFLAG_UNMASKED_LINK_PARENT
) & ~64)) {
534 i
.second
.unmasked
->SetValue(false);
535 i
.second
.unmasked
->Disable();
537 i
.second
.unmasked
->Enable();
538 //Pressed is to be enabled if:
539 //- This modifier is unmasked or parent is unmasked.
540 //- Parent nor child is not pressed.
541 if(((i
.second
.tmpflags
& (TMPFLAG_UNMASKED
| TMPFLAG_UNMASKED_LINK_PARENT
|
542 TMPFLAG_PRESSED_LINK_CHILD
| TMPFLAG_PRESSED_LINK_PARENT
)) & 112) == 64)
543 i
.second
.pressed
->Enable();
545 i
.second
.pressed
->SetValue(false);
546 i
.second
.pressed
->Disable();
551 void wxdialog_keyentry::on_ok(wxCommandEvent
& e
)
556 void wxdialog_keyentry::on_cancel(wxCommandEvent
& e
)
558 EndModal(wxID_CANCEL
);
561 std::string
wxdialog_keyentry::getkey()
566 for(auto i
: modifiers
) {
567 if(i
.second
.pressed
->GetValue()) {
576 for(auto i
: modifiers
) {
577 if(i
.second
.unmasked
->GetValue()) {
584 x
= x
+ "|" + tostdstring(mainkey
->GetValue());
589 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
592 struct emu_args
* a
= new emu_args
;
595 a
->load_has_to_succeed
= false;
596 platform::set_modal_pause(true);
597 emulation_thread
= &thread::create(emulator_main
, a
);
598 main_window
= new wxwin_mainwindow();
600 platform::set_modal_pause(false);
601 } catch(std::bad_alloc
& e
) {
606 wxwin_mainwindow::panel::panel(wxWindow
* win
)
609 initialize_wx_keyboard();
610 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
611 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
612 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
613 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
614 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
615 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
616 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
617 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
618 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
619 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
620 SetMinSize(wxSize(512, 448));
623 void wxwin_mainwindow::menu_start(wxString name
)
625 while(!upper
.empty())
627 current_menu
= new wxMenu();
628 menubar
->Append(current_menu
, name
);
631 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
633 while(!upper
.empty())
635 menubar
->Append(menu
, name
);
639 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
641 current_menu
->AppendSubMenu(menu
, name
);
644 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
646 current_menu
->Append(id
, name
);
647 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
648 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
651 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
653 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
654 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
655 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
658 void wxwin_mainwindow::menu_start_sub(wxString name
)
660 wxMenu
* old
= current_menu
;
661 upper
.push(current_menu
);
662 current_menu
= new wxMenu();
663 old
->AppendSubMenu(current_menu
, name
);
666 void wxwin_mainwindow::menu_end_sub(wxString name
)
668 current_menu
= upper
.top();
672 bool wxwin_mainwindow::menu_ischecked(int id
)
674 if(checkitems
.count(id
))
675 return checkitems
[id
]->IsChecked();
680 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
682 if(checkitems
.count(id
))
683 return checkitems
[id
]->Check(newstate
);
688 void wxwin_mainwindow::menu_separator()
690 current_menu
->AppendSeparator();
693 void wxwin_mainwindow::panel::request_paint()
698 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
700 render_framebuffer();
701 static struct SwsContext
* ctx
;
707 if(!screen_buffer
|| main_screen
.width
!= old_width
|| main_screen
.height
!= old_height
) {
709 delete[] screen_buffer
;
710 screen_buffer
= new unsigned char[main_screen
.width
* main_screen
.height
* 3];
711 old_height
= main_screen
.height
;
712 old_width
= main_screen
.width
;
713 uint32_t w
= main_screen
.width
;
714 uint32_t h
= main_screen
.height
;
716 ctx
= sws_getCachedContext(ctx
, w
, h
, PIX_FMT_RGBA
, w
, h
, PIX_FMT_BGR24
, SWS_POINT
|
717 SWS_CPU_CAPS_MMX2
, NULL
, NULL
, NULL
);
722 SetMinSize(wxSize(w
, h
));
725 srcs
[0] = 4 * main_screen
.width
;
726 dsts
[0] = 3 * main_screen
.width
;
727 srcp
[0] = reinterpret_cast<unsigned char*>(main_screen
.memory
);
728 dstp
[0] = screen_buffer
;
729 memset(screen_buffer
, 0, main_screen
.width
* main_screen
.height
* 3);
730 uint64_t t1
= get_utime();
731 if(main_screen
.width
&& main_screen
.height
)
732 sws_scale(ctx
, srcp
, srcs
, 0, main_screen
.height
, dstp
, dsts
);
733 uint64_t t2
= get_utime();
734 wxBitmap
bmp(wxImage(main_screen
.width
, main_screen
.height
, screen_buffer
, true));
735 uint64_t t3
= get_utime();
736 dc
.DrawBitmap(bmp
, 0, 0, false);
737 main_window_dirty
= false;
740 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
745 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
747 handle_wx_keyboard(e
, true);
750 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
752 handle_wx_keyboard(e
, false);
755 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
760 wxwin_mainwindow::wxwin_mainwindow()
761 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
762 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
764 broadcast_listener
* blistener
= new broadcast_listener(this);
766 wxFlexGridSizer
* toplevel
= new wxFlexGridSizer(1, 1, 0, 0);
767 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
768 toplevel
->SetSizeHints(this);
772 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
773 menubar
= new wxMenuBar
;
776 //TOP-level accels: ACFOS.
777 //System menu: (ACFOS)EMNPQRU
778 menu_start(wxT("&System"));
779 menu_entry(wxID_FRAMEADVANCE
, wxT("Fra&me advance"));
780 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("S&ubframe advance"));
781 menu_entry(wxID_NEXTPOLL
, wxT("&Next poll"));
782 menu_entry(wxID_PAUSE
, wxT("&Pause/Unpause"));
784 menu_entry(wxID_ERESET
, wxT("&Reset"));
786 menu_entry(wxID_EDIT_AUTHORS
, wxT("&Edit game name && authors"));
788 menu_entry(wxID_EXIT
, wxT("&Quit"));
790 menu_entry(wxID_ABOUT
, wxT("About"));
791 //File menu: (ACFOS)DELMNPRTUVW
792 menu_start(wxT("&File"));
793 menu_entry_check(wxID_READONLY_MODE
, wxT("Reado&nly mode"));
794 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
796 menu_entry(wxID_SAVE_STATE
, wxT("Save stat&e"));
797 menu_entry(wxID_SAVE_MOVIE
, wxT("Sa&ve movie"));
799 menu_entry(wxID_LOAD_STATE
, wxT("&Load state"));
800 menu_entry(wxID_LOAD_STATE_RO
, wxT("Loa&d state (readonly)"));
801 menu_entry(wxID_LOAD_STATE_RW
, wxT("Load s&tate (read-write)"));
802 menu_entry(wxID_LOAD_STATE_P
, wxT("Load state (&preserve)"));
803 menu_entry(wxID_LOAD_MOVIE
, wxT("Load &movie"));
804 menu_entry(wxID_REWIND_MOVIE
, wxT("Re&wind movie"));
806 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Save sc&reenshot"));
808 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
809 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
810 //Autohold menu: (ACFOS)
811 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
812 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
813 //Scripting menu: (ACFOS)ERU
814 menu_start(wxT("S&cripting"));
815 menu_entry(wxID_RUN_SCRIPT
, wxT("&Run script"));
818 menu_entry(wxID_EVAL_LUA
, wxT("&Evaluate Lua statement"));
819 menu_entry(wxID_RUN_LUA
, wxT("R&un Lua script"));
822 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch"));
824 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch"));
825 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch"));
826 //Settings menu: (ACFOS)
827 menu_start(wxT("Settings"));
828 menu_entry(wxID_EDIT_AXES
, wxT("Configure axes"));
829 menu_entry(wxID_EDIT_SETTINGS
, wxT("Configure settings"));
830 menu_entry(wxID_EDIT_KEYBINDINGS
, wxT("Configure keybindings"));
831 menu_entry(wxID_EDIT_ALIAS
, wxT("Configure aliases"));
832 if(platform::sound_initialized()) {
833 //Sound menu: (ACFOS)EHU
834 menu_start(wxT("S&ound"));
835 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("So&unds enabled"));
836 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
837 menu_entry(wxID_SHOW_AUDIO_STATUS
, wxT("S&how audio status"));
839 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
840 new sound_select_menu(this)));
841 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
845 void wxwin_mainwindow::request_paint()
850 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
852 //Veto it for now, latter things will delete it.
854 platform::queue("quit-emulator");
857 void wxwin_mainwindow::notify_update() throw()
859 if(!main_window_dirty
) {
860 main_window_dirty
= true;
865 void wxwin_mainwindow::notify_exit() throw()
867 join_emulator_thread();
871 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
874 wxTextEntryDialog
* d2
;
875 std::string filename
;
878 case wxID_FRAMEADVANCE
:
879 platform::queue("+advance-frame");
880 platform::queue("-advance-frame");
882 case wxID_SUBFRAMEADVANCE
:
883 platform::queue("+advance-poll");
884 platform::queue("-advance-poll");
887 platform::queue("advance-skiplag");
890 platform::queue("pause-emulator");
893 platform::queue("reset");
896 platform::queue("quit-emulator");
898 case wxID_AUDIO_ENABLED
:
899 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
901 case wxID_SHOW_AUDIO_STATUS
:
902 platform::queue("show-sound-status");
904 case wxID_LOAD_MOVIE
:
905 d
= new wxFileDialog(this, wxT("Load Movie"), wxT("."));
906 if(d
->ShowModal() == wxID_OK
)
907 filename
= tostdstring(d
->GetPath());
911 platform::queue("load-movie " + filename
);
913 case wxID_LOAD_STATE
:
914 d
= new wxFileDialog(this, wxT("Load State"), wxT("."));
915 if(d
->ShowModal() == wxID_OK
)
916 filename
= tostdstring(d
->GetPath());
920 platform::queue("load " + filename
);
922 case wxID_LOAD_STATE_RO
:
923 d
= new wxFileDialog(this, wxT("Load State (Read-Only)"), wxT("."));
924 if(d
->ShowModal() == wxID_OK
)
925 filename
= tostdstring(d
->GetPath());
929 platform::queue("load-readonly " + filename
);
931 case wxID_LOAD_STATE_RW
:
932 d
= new wxFileDialog(this, wxT("Load State (Read-Write)"), wxT("."));
933 if(d
->ShowModal() == wxID_OK
)
934 filename
= tostdstring(d
->GetPath());
938 platform::queue("load-state " + filename
);
940 case wxID_REWIND_MOVIE
:
941 platform::queue("rewind-movie");
943 case wxID_SAVE_MOVIE
:
944 d
= new wxFileDialog(this, wxT("Save Movie"), wxT("."));
945 if(d
->ShowModal() == wxID_OK
)
946 filename
= tostdstring(d
->GetPath());
950 platform::queue("save-movie " + filename
);
952 case wxID_SAVE_STATE
:
953 d
= new wxFileDialog(this, wxT("Save State"), wxT("."));
954 if(d
->ShowModal() == wxID_OK
)
955 filename
= tostdstring(d
->GetPath());
959 platform::queue("save-state " + filename
);
961 case wxID_SAVE_SCREENSHOT
:
962 d
= new wxFileDialog(this, wxT("Save Screenshot"), wxT("."));
963 if(d
->ShowModal() == wxID_OK
)
964 filename
= tostdstring(d
->GetPath());
968 platform::queue("take-screenshot " + filename
);
970 case wxID_RUN_SCRIPT
:
971 d
= new wxFileDialog(this, wxT("Select Script"), wxT("."));
972 if(d
->ShowModal() == wxID_OK
)
973 filename
= tostdstring(d
->GetPath());
977 platform::queue("run-script " + filename
);
980 d
= new wxFileDialog(this, wxT("Select Lua Script"), wxT("."));
981 if(d
->ShowModal() == wxID_OK
)
982 filename
= tostdstring(d
->GetPath());
986 platform::queue("run-lua " + filename
);
989 d2
= new wxTextEntryDialog(this, wxT("Enter Lua statement:"), wxT("Evaluate Lua"));
990 if(d2
->ShowModal() == wxID_OK
)
991 filename
= tostdstring(d2
->GetValue());
993 platform::queue("evaluate-lua " + filename
);
995 case wxID_READONLY_MODE
:
996 s
= menu_ischecked(wxID_READONLY_MODE
);
997 platform::queue(_set_readonly
, &s
, true);
1000 wxeditor_axes_display(this);
1002 case wxID_EDIT_AUTHORS
:
1003 wxeditor_authors_display(this);
1005 case wxID_EDIT_SETTINGS
:
1006 wxeditor_settings_display(this);
1008 case wxID_EDIT_KEYBINDINGS
:
1009 menu_edit_keybindings(e
);
1011 case wxID_EDIT_ALIAS
:
1012 menu_edit_aliases(e
);
1014 case wxID_EDIT_MEMORYWATCH
:
1015 menu_edit_memorywatch(e
);
1017 case wxID_SAVE_MEMORYWATCH
:
1018 menu_save_memorywatch(e
);
1020 case wxID_LOAD_MEMORYWATCH
:
1021 menu_load_memorywatch(e
);
1024 std::ostringstream str
;
1025 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1026 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1027 str
<< "Core: " << bsnes_core_version
<< std::endl
;
1028 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1034 #define NEW_KEYBINDING "A new binding..."
1035 #define NEW_ALIAS "A new alias..."
1036 #define NEW_WATCH "A new watch..."
1038 void wxwin_mainwindow::menu_edit_keybindings(wxCommandEvent
& e
)
1040 platform::set_modal_pause(true);
1041 std::set
<std::string
> bind
;
1042 runemufn([&bind
]() { bind
= keymapper::get_bindings(); });
1043 std::vector
<wxString
> choices
;
1044 choices
.push_back(wxT(NEW_KEYBINDING
));
1046 choices
.push_back(towxstring(i
));
1047 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1048 wxT("Select binding"), choices
.size(), &choices
[0]);
1049 if(d
->ShowModal() == wxID_CANCEL
) {
1051 platform::set_modal_pause(false);
1054 std::string key
= tostdstring(d
->GetStringSelection());
1056 if(key
== NEW_KEYBINDING
) {
1057 wxdialog_keyentry
* d2
= new wxdialog_keyentry(this);
1058 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1059 // wxT("Edit binding"), wxT(""));
1060 if(d2
->ShowModal() == wxID_CANCEL
) {
1062 platform::set_modal_pause(false);
1066 //key = tostdstring(d2->GetValue());
1069 std::string old_command_value
;
1070 runemufn([&old_command_value
, key
]() { old_command_value
= keymapper::get_command_for(key
); });
1071 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1072 towxstring(old_command_value
));
1073 if(d4
->ShowModal() == wxID_CANCEL
) {
1075 platform::set_modal_pause(false);
1079 std::string faulttext
;
1080 std::string newcommand
= tostdstring(d4
->GetValue());
1081 runemufn([&fault
, &faulttext
, key
, newcommand
]() {
1083 keymapper::bind_for(key
, newcommand
);
1084 } catch(std::exception
& e
) {
1088 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1089 faulttext
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1094 platform::set_modal_pause(false);
1097 void wxwin_mainwindow::menu_edit_aliases(wxCommandEvent
& e
)
1099 platform::set_modal_pause(true);
1100 std::set
<std::string
> bind
;
1101 runemufn([&bind
]() { bind
= command::get_aliases(); });
1102 std::vector
<wxString
> choices
;
1103 choices
.push_back(wxT(NEW_ALIAS
));
1105 choices
.push_back(towxstring(i
));
1106 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1107 wxT("Select alias"), choices
.size(), &choices
[0]);
1108 if(d
->ShowModal() == wxID_CANCEL
) {
1110 platform::set_modal_pause(false);
1113 std::string alias
= tostdstring(d
->GetStringSelection());
1115 if(alias
== NEW_ALIAS
) {
1116 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1117 wxT("Enter alias name"));
1118 if(d2
->ShowModal() == wxID_CANCEL
) {
1120 platform::set_modal_pause(false);
1123 alias
= tostdstring(d2
->GetValue());
1125 if(!command::valid_alias_name(alias
)) {
1126 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1127 "name: ") + alias
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1130 platform::set_modal_pause(false);
1134 std::string old_alias_value
= command::get_alias_for(alias
);
1135 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1136 towxstring(old_alias_value
), wxOK
| wxCANCEL
| wxCENTRE
| wxTE_MULTILINE
);
1137 if(d4
->ShowModal() == wxID_CANCEL
) {
1139 platform::set_modal_pause(false);
1142 std::string newcmd
= tostdstring(d4
->GetValue());
1143 runemufn([alias
, newcmd
]() { command::set_alias_for(alias
, newcmd
); });
1145 platform::set_modal_pause(false);
1148 void wxwin_mainwindow::menu_load_memorywatch(wxCommandEvent
& e
)
1150 platform::set_modal_pause(true);
1151 std::set
<std::string
> old_watches
;
1152 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1153 std::map
<std::string
, std::string
> new_watches
;
1154 std::string filename
;
1156 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1157 if(d
->ShowModal() == wxID_CANCEL
) {
1159 platform::set_modal_pause(false);
1162 filename
= tostdstring(d
->GetPath());
1164 //Did we pick a .zip file?
1166 zip_reader
zr(filename
);
1167 std::vector
<wxString
> files
;
1169 files
.push_back(towxstring(i
));
1170 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1171 wxT("Select member"), files
.size(), &files
[0]);
1172 if(d2
->ShowModal() == wxID_CANCEL
) {
1174 platform::set_modal_pause(false);
1177 filename
= filename
+ "/" + tostdstring(d2
->GetStringSelection());
1184 std::istream
& in
= open_file_relative(filename
, "");
1188 std::getline(in
, wname
);
1189 std::getline(in
, wexpr
);
1190 new_watches
[wname
] = wexpr
;
1193 } catch(std::exception
& e
) {
1194 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1195 "watch: ") + e
.what()), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1198 platform::set_modal_pause(false);
1202 runemufn([&new_watches
, &old_watches
]() {
1203 for(auto i
: new_watches
)
1204 set_watchexpr_for(i
.first
, i
.second
);
1205 for(auto i
: old_watches
)
1206 if(!new_watches
.count(i
))
1207 set_watchexpr_for(i
, "");
1209 platform::set_modal_pause(false);
1212 void wxwin_mainwindow::menu_save_memorywatch(wxCommandEvent
& e
)
1214 platform::set_modal_pause(true);
1215 std::set
<std::string
> old_watches
;
1216 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1217 std::string filename
;
1219 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1220 if(d
->ShowModal() == wxID_CANCEL
) {
1222 platform::set_modal_pause(false);
1225 filename
= tostdstring(d
->GetPath());
1228 std::ofstream
out(filename
.c_str());
1229 for(auto i
: old_watches
)
1230 out
<< i
<< std::endl
<< get_watchexpr_for(i
) << std::endl
;
1232 platform::set_modal_pause(false);
1236 void wxwin_mainwindow::menu_edit_memorywatch(wxCommandEvent
& e
)
1238 platform::set_modal_pause(true);
1239 std::set
<std::string
> bind
;
1240 runemufn([&bind
]() { bind
= get_watches(); });
1241 std::vector
<wxString
> choices
;
1242 choices
.push_back(wxT(NEW_WATCH
));
1244 choices
.push_back(towxstring(i
));
1245 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1246 wxT("Select watch"), choices
.size(), &choices
[0]);
1247 if(d
->ShowModal() == wxID_CANCEL
) {
1249 platform::set_modal_pause(false);
1252 std::string watch
= tostdstring(d
->GetStringSelection());
1254 if(watch
== NEW_WATCH
) {
1255 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1256 wxT("Enter watch name"));
1257 if(d2
->ShowModal() == wxID_CANCEL
) {
1259 platform::set_modal_pause(false);
1262 watch
= tostdstring(d2
->GetValue());
1265 std::string old_watch_value
= get_watchexpr_for(watch
);
1266 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1267 towxstring(old_watch_value
), wxOK
| wxCANCEL
| wxCENTRE
);
1268 if(d4
->ShowModal() == wxID_CANCEL
) {
1270 platform::set_modal_pause(false);
1273 std::string newexpr
= tostdstring(d4
->GetValue());
1274 runemufn([watch
, newexpr
]() { set_watchexpr_for(watch
, newexpr
); });
1275 platform::set_modal_pause(false);