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 keygroup
mouse_x("mouse_x", keygroup::KT_MOUSE
);
122 keygroup
mouse_y("mouse_y", keygroup::KT_MOUSE
);
123 keygroup
mouse_l("mouse_left", keygroup::KT_KEY
);
124 keygroup
mouse_m("mouse_center", keygroup::KT_KEY
);
125 keygroup
mouse_r("mouse_right", keygroup::KT_KEY
);
126 keygroup
mouse_i("mouse_inwindow", keygroup::KT_KEY
);
128 void handle_wx_mouse(wxMouseEvent
& e
)
130 platform::queue(keypress(modifier_set(), mouse_x
, e
.GetX()));
131 platform::queue(keypress(modifier_set(), mouse_y
, e
.GetY()));
133 platform::queue(keypress(modifier_set(), mouse_i
, 1));
135 platform::queue(keypress(modifier_set(), mouse_i
, 0));
137 platform::queue(keypress(modifier_set(), mouse_l
, 1));
139 platform::queue(keypress(modifier_set(), mouse_l
, 0));
141 platform::queue(keypress(modifier_set(), mouse_m
, 1));
143 platform::queue(keypress(modifier_set(), mouse_m
, 0));
145 platform::queue(keypress(modifier_set(), mouse_r
, 1));
147 platform::queue(keypress(modifier_set(), mouse_r
, 0));
150 bool is_readonly_mode()
153 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
157 bool UI_get_autohold(unsigned pid
, unsigned idx
)
160 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
164 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
166 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
169 int UI_controller_index_by_logical(unsigned lid
)
172 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
176 int UI_button_id(unsigned pcid
, unsigned lidx
)
179 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
183 class controller_autohold_menu
: public wxMenu
186 controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
);
189 void on_select(wxCommandEvent
& e
);
190 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
193 wxMenuItem
* entries
[MAX_LOGICAL_BUTTONS
];
194 unsigned enabled_entries
;
197 class autohold_menu
: public wxMenu
200 autohold_menu(wxwin_mainwindow
* win
);
202 void on_select(wxCommandEvent
& e
);
203 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
205 controller_autohold_menu
* menus
[MAXCONTROLLERS
];
206 wxMenuItem
* entries
[MAXCONTROLLERS
];
209 class sound_select_menu
: public wxMenu
212 sound_select_menu(wxwin_mainwindow
* win
);
213 void update(const std::string
& dev
);
214 void on_select(wxCommandEvent
& e
);
216 std::map
<std::string
, wxMenuItem
*> items
;
217 std::map
<int, std::string
> devices
;
220 class sound_select_menu
;
222 class broadcast_listener
: public information_dispatch
225 broadcast_listener(wxwin_mainwindow
* win
);
226 void set_sound_select(sound_select_menu
* sdev
);
227 void set_autohold_menu(autohold_menu
* ah
);
228 void on_sound_unmute(bool unmute
) throw();
229 void on_sound_change(const std::string
& dev
) throw();
230 void on_mode_change(bool readonly
) throw();
231 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
232 void on_autohold_reconfigure();
234 wxwin_mainwindow
* mainw
;
235 sound_select_menu
* sounddev
;
236 autohold_menu
* ahmenu
;
239 controller_autohold_menu::controller_autohold_menu(unsigned lid
, enum devicetype_t dtype
)
241 platform::set_modal_pause(true);
243 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
244 int id
= wxID_AUTOHOLD_FIRST
+ MAX_LOGICAL_BUTTONS
* lid
+ i
;
245 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
248 platform::set_modal_pause(false);
251 void controller_autohold_menu::change_type()
254 int pid
= controls
.lcid_to_pcid(our_lid
);
255 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
258 pidx
= controls
.button_id(pid
, i
);
260 entries
[i
]->Check(pid
> 0 && UI_get_autohold(pid
, pidx
));
261 entries
[i
]->Enable();
264 entries
[i
]->Check(false);
265 entries
[i
]->Enable(false);
270 bool controller_autohold_menu::is_dummy()
272 return !enabled_entries
;
275 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
278 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* MAX_LOGICAL_BUTTONS
|| x
>= wxID_AUTOHOLD_FIRST
*
279 (our_lid
+ 1) * MAX_LOGICAL_BUTTONS
) {
282 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % MAX_LOGICAL_BUTTONS
;
283 platform::set_modal_pause(true);
284 int pid
= controls
.lcid_to_pcid(our_lid
);
285 if(pid
< 0 || !entries
[lidx
]) {
286 platform::set_modal_pause(false);
289 int pidx
= controls
.button_id(pid
, lidx
);
291 platform::set_modal_pause(false);
294 //Autohold change on pid=pid, ctrlindx=idx, state
295 bool newstate
= entries
[lidx
]->IsChecked();
296 UI_change_autohold(pid
, pidx
, newstate
);
297 platform::set_modal_pause(false);
300 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
302 platform::set_modal_pause(true);
303 int pid2
= UI_controller_index_by_logical(our_lid
);
304 if(pid2
< 0 || static_cast<unsigned>(pid
) != pid2
) {
305 platform::set_modal_pause(false);
308 for(unsigned i
= 0; i
< MAX_LOGICAL_BUTTONS
; i
++) {
309 int idx
= UI_button_id(pid2
, i
);
310 if(idx
< 0 || static_cast<unsigned>(idx
) != ctrlnum
)
312 entries
[i
]->Check(newstate
);
314 platform::set_modal_pause(false);
318 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
320 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
321 std::ostringstream str
;
322 str
<< "Controller #&" << (i
+ 1);
323 menus
[i
] = new controller_autohold_menu(i
, DT_NONE
);
324 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
325 entries
[i
]->Enable(!menus
[i
]->is_dummy());
327 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
328 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
332 void autohold_menu::reconfigure()
334 platform::set_modal_pause(true);
335 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++) {
336 menus
[i
]->change_type();
337 entries
[i
]->Enable(!menus
[i
]->is_dummy());
339 platform::set_modal_pause(false);
342 void autohold_menu::on_select(wxCommandEvent
& e
)
344 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
345 menus
[i
]->on_select(e
);
348 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
350 for(unsigned i
= 0; i
< MAXCONTROLLERS
; i
++)
351 menus
[i
]->update(pid
, ctrlnum
, newstate
);
354 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
356 std::string curdev
= platform::get_sound_device();
357 int j
= wxID_AUDIODEV_FIRST
;
358 for(auto i
: platform::get_sound_devices()) {
359 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
360 devices
[j
] = i
.first
;
361 if(i
.first
== curdev
)
362 items
[i
.first
]->Check();
363 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
364 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
369 void sound_select_menu::update(const std::string
& dev
)
374 void _do_sound_select(void* args)
376 std::string* x = reinterpret_cast<std::string*>(args);
377 platform::set_sound_device(*x);
380 void sound_select_menu::on_select(wxCommandEvent
& e
)
382 std::string devname
= devices
[e
.GetId()];
384 runemufn([devname
]() { platform::set_sound_device(devname
); });
387 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
388 : information_dispatch("wxwidgets-broadcast-listener")
393 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
398 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
403 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
405 runuifun([unmute
, mainw
]() { mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
408 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
410 runuifun([dev
, sounddev
]() { if(sounddev
) sounddev
->update(dev
); });
413 void broadcast_listener::on_mode_change(bool readonly
) throw()
415 runuifun([readonly
, mainw
]() { mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
418 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
420 runuifun([pid
, ctrlnum
, newstate
, ahmenu
]() { ahmenu
->update(pid
, ctrlnum
, newstate
); });
423 void broadcast_listener::on_autohold_reconfigure()
425 runuifun([ahmenu
]() { ahmenu
->reconfigure(); });
428 void _set_readonly(void* args
)
430 bool s
= *reinterpret_cast<bool*>(args
);
431 movb
.get_movie().readonly_mode(s
);
433 lua_callback_do_readwrite();
434 update_movie_state();
437 struct keyentry_mod_data
440 wxCheckBox
* unmasked
;
444 class wxdialog_keyentry
: public wxDialog
447 wxdialog_keyentry(wxWindow
* parent
);
448 void on_change_setting(wxCommandEvent
& e
);
449 void on_ok(wxCommandEvent
& e
);
450 void on_cancel(wxCommandEvent
& e
);
451 std::string
getkey();
453 std::map
<std::string
, keyentry_mod_data
> modifiers
;
459 wxdialog_keyentry::wxdialog_keyentry(wxWindow
* parent
)
460 : wxDialog(parent
, wxID_ANY
, wxT("Specify key"), wxDefaultPosition
, wxSize(-1, -1))
462 std::vector
<wxString
> keych
;
463 std::set
<std::string
> mods
, keys
;
465 runemufn([&mods
, &keys
]() { mods
= modifier::get_set(); keys
= keygroup::get_keys(); });
467 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
470 wxFlexGridSizer
* t_s
= new wxFlexGridSizer(mods
.size() + 1, 3, 0, 0);
472 t_s
->Add(new wxStaticText(this, wxID_ANY
, towxstring(i
)), 0, wxGROW
);
474 t_s
->Add(m
.pressed
= new wxCheckBox(this, wxID_ANY
, wxT("Pressed")), 0, wxGROW
);
475 t_s
->Add(m
.unmasked
= new wxCheckBox(this, wxID_ANY
, wxT("Unmasked")), 0, wxGROW
);
476 m
.pressed
->Disable();
478 m
.pressed
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
479 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
480 m
.unmasked
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
481 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
484 keych
.push_back(towxstring(i
));
485 t_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Key")), 0, wxGROW
);
486 t_s
->Add(mainkey
= new wxComboBox(this, wxID_ANY
, keych
[0], wxDefaultPosition
, wxDefaultSize
,
487 keych
.size(), &keych
[0], wxCB_READONLY
), 1, wxGROW
);
488 mainkey
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
489 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
492 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
493 pbutton_s
->AddStretchSpacer();
494 pbutton_s
->Add(ok
= new wxButton(this, wxID_OK
, wxT("OK")), 0, wxGROW
);
495 pbutton_s
->Add(cancel
= new wxButton(this, wxID_CANCEL
, wxT("Cancel")), 0, wxGROW
);
496 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
497 wxCommandEventHandler(wxdialog_keyentry::on_ok
), NULL
, this);
498 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
499 wxCommandEventHandler(wxdialog_keyentry::on_cancel
), NULL
, this);
500 top_s
->Add(pbutton_s
, 0, wxGROW
);
502 t_s
->SetSizeHints(this);
503 top_s
->SetSizeHints(this);
507 #define TMPFLAG_UNMASKED 65
508 #define TMPFLAG_UNMASKED_LINK_CHILD 2
509 #define TMPFLAG_UNMASKED_LINK_PARENT 68
510 #define TMPFLAG_PRESSED 8
511 #define TMPFLAG_PRESSED_LINK_CHILD 16
512 #define TMPFLAG_PRESSED_LINK_PARENT 32
514 void wxdialog_keyentry::on_change_setting(wxCommandEvent
& e
)
516 for(auto& i
: modifiers
)
517 i
.second
.tmpflags
= 0;
518 for(auto& i
: modifiers
) {
521 m
= &modifier::lookup(i
.first
);
523 i
.second
.pressed
->Disable();
524 i
.second
.unmasked
->Disable();
527 std::string j
= m
->linked_name();
528 if(i
.second
.unmasked
->GetValue())
529 i
.second
.tmpflags
|= TMPFLAG_UNMASKED
;
531 if(modifiers
[j
].unmasked
->GetValue())
532 i
.second
.tmpflags
|= TMPFLAG_UNMASKED_LINK_PARENT
;
533 if(i
.second
.unmasked
->GetValue())
534 modifiers
[j
].tmpflags
|= TMPFLAG_UNMASKED_LINK_CHILD
;
536 if(i
.second
.pressed
->GetValue())
537 i
.second
.tmpflags
|= TMPFLAG_PRESSED
;
539 if(modifiers
[j
].pressed
->GetValue())
540 i
.second
.tmpflags
|= TMPFLAG_PRESSED_LINK_PARENT
;
541 if(i
.second
.pressed
->GetValue())
542 modifiers
[j
].tmpflags
|= TMPFLAG_PRESSED_LINK_CHILD
;
545 for(auto& i
: modifiers
) {
546 //Unmasked is to be enabled if neither unmasked link flag is set.
547 if(i
.second
.tmpflags
& ((TMPFLAG_UNMASKED_LINK_CHILD
| TMPFLAG_UNMASKED_LINK_PARENT
) & ~64)) {
548 i
.second
.unmasked
->SetValue(false);
549 i
.second
.unmasked
->Disable();
551 i
.second
.unmasked
->Enable();
552 //Pressed is to be enabled if:
553 //- This modifier is unmasked or parent is unmasked.
554 //- Parent nor child is not pressed.
555 if(((i
.second
.tmpflags
& (TMPFLAG_UNMASKED
| TMPFLAG_UNMASKED_LINK_PARENT
|
556 TMPFLAG_PRESSED_LINK_CHILD
| TMPFLAG_PRESSED_LINK_PARENT
)) & 112) == 64)
557 i
.second
.pressed
->Enable();
559 i
.second
.pressed
->SetValue(false);
560 i
.second
.pressed
->Disable();
565 void wxdialog_keyentry::on_ok(wxCommandEvent
& e
)
570 void wxdialog_keyentry::on_cancel(wxCommandEvent
& e
)
572 EndModal(wxID_CANCEL
);
575 std::string
wxdialog_keyentry::getkey()
580 for(auto i
: modifiers
) {
581 if(i
.second
.pressed
->GetValue()) {
590 for(auto i
: modifiers
) {
591 if(i
.second
.unmasked
->GetValue()) {
598 x
= x
+ "|" + tostdstring(mainkey
->GetValue());
603 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
606 struct emu_args
* a
= new emu_args
;
609 a
->load_has_to_succeed
= false;
610 platform::set_modal_pause(true);
611 emulation_thread
= &thread::create(emulator_main
, a
);
612 main_window
= new wxwin_mainwindow();
614 platform::set_modal_pause(false);
615 } catch(std::bad_alloc
& e
) {
620 wxwin_mainwindow::panel::panel(wxWindow
* win
)
623 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
624 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
625 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
626 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
627 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
628 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
629 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
630 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
631 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
632 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
633 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
634 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
635 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
636 SetMinSize(wxSize(512, 448));
639 void wxwin_mainwindow::menu_start(wxString name
)
641 while(!upper
.empty())
643 current_menu
= new wxMenu();
644 menubar
->Append(current_menu
, name
);
647 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
649 while(!upper
.empty())
651 menubar
->Append(menu
, name
);
655 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
657 current_menu
->AppendSubMenu(menu
, name
);
660 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
662 current_menu
->Append(id
, name
);
663 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
664 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
667 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
669 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
670 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
671 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
674 void wxwin_mainwindow::menu_start_sub(wxString name
)
676 wxMenu
* old
= current_menu
;
677 upper
.push(current_menu
);
678 current_menu
= new wxMenu();
679 old
->AppendSubMenu(current_menu
, name
);
682 void wxwin_mainwindow::menu_end_sub(wxString name
)
684 current_menu
= upper
.top();
688 bool wxwin_mainwindow::menu_ischecked(int id
)
690 if(checkitems
.count(id
))
691 return checkitems
[id
]->IsChecked();
696 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
698 if(checkitems
.count(id
))
699 return checkitems
[id
]->Check(newstate
);
704 void wxwin_mainwindow::menu_separator()
706 current_menu
->AppendSeparator();
709 void wxwin_mainwindow::panel::request_paint()
714 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
716 render_framebuffer();
717 static struct SwsContext
* ctx
;
723 if(!screen_buffer
|| main_screen
.width
!= old_width
|| main_screen
.height
!= old_height
) {
725 delete[] screen_buffer
;
726 screen_buffer
= new unsigned char[main_screen
.width
* main_screen
.height
* 3];
727 old_height
= main_screen
.height
;
728 old_width
= main_screen
.width
;
729 uint32_t w
= main_screen
.width
;
730 uint32_t h
= main_screen
.height
;
732 ctx
= sws_getCachedContext(ctx
, w
, h
, PIX_FMT_RGBA
, w
, h
, PIX_FMT_BGR24
, SWS_POINT
|
733 SWS_CPU_CAPS_MMX2
, NULL
, NULL
, NULL
);
738 SetMinSize(wxSize(w
, h
));
741 srcs
[0] = 4 * main_screen
.width
;
742 dsts
[0] = 3 * main_screen
.width
;
743 srcp
[0] = reinterpret_cast<unsigned char*>(main_screen
.memory
);
744 dstp
[0] = screen_buffer
;
745 memset(screen_buffer
, 0, main_screen
.width
* main_screen
.height
* 3);
746 uint64_t t1
= get_utime();
747 if(main_screen
.width
&& main_screen
.height
)
748 sws_scale(ctx
, srcp
, srcs
, 0, main_screen
.height
, dstp
, dsts
);
749 uint64_t t2
= get_utime();
750 wxBitmap
bmp(wxImage(main_screen
.width
, main_screen
.height
, screen_buffer
, true));
751 uint64_t t3
= get_utime();
752 dc
.DrawBitmap(bmp
, 0, 0, false);
753 main_window_dirty
= false;
756 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
761 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
763 handle_wx_keyboard(e
, true);
766 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
768 handle_wx_keyboard(e
, false);
771 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
776 wxwin_mainwindow::wxwin_mainwindow()
777 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
778 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
780 broadcast_listener
* blistener
= new broadcast_listener(this);
782 wxFlexGridSizer
* toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
783 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
784 toplevel
->Add(spanel
= new wxwin_status::panel(this, 20), 1, wxGROW
);
785 toplevel
->SetSizeHints(this);
789 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
790 menubar
= new wxMenuBar
;
793 //TOP-level accels: ACFOS.
794 //System menu: (ACFOS)EMNPQRU
795 menu_start(wxT("&System"));
796 menu_entry(wxID_FRAMEADVANCE
, wxT("Fra&me advance"));
797 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("S&ubframe advance"));
798 menu_entry(wxID_NEXTPOLL
, wxT("&Next poll"));
799 menu_entry(wxID_PAUSE
, wxT("&Pause/Unpause"));
801 menu_entry(wxID_ERESET
, wxT("&Reset"));
803 menu_entry(wxID_EDIT_AUTHORS
, wxT("&Edit game name && authors"));
805 menu_entry(wxID_EXIT
, wxT("&Quit"));
807 menu_entry(wxID_ABOUT
, wxT("About"));
808 //File menu: (ACFOS)DELMNPRTUVW
809 menu_start(wxT("&File"));
810 menu_entry_check(wxID_READONLY_MODE
, wxT("Reado&nly mode"));
811 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
813 menu_entry(wxID_SAVE_STATE
, wxT("Save stat&e"));
814 menu_entry(wxID_SAVE_MOVIE
, wxT("Sa&ve movie"));
816 menu_entry(wxID_LOAD_STATE
, wxT("&Load state"));
817 menu_entry(wxID_LOAD_STATE_RO
, wxT("Loa&d state (readonly)"));
818 menu_entry(wxID_LOAD_STATE_RW
, wxT("Load s&tate (read-write)"));
819 menu_entry(wxID_LOAD_STATE_P
, wxT("Load state (&preserve)"));
820 menu_entry(wxID_LOAD_MOVIE
, wxT("Load &movie"));
821 menu_entry(wxID_REWIND_MOVIE
, wxT("Re&wind movie"));
823 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Save sc&reenshot"));
825 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
826 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
827 //Autohold menu: (ACFOS)
828 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
829 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
830 //Scripting menu: (ACFOS)ERU
831 menu_start(wxT("S&cripting"));
832 menu_entry(wxID_RUN_SCRIPT
, wxT("&Run script"));
835 menu_entry(wxID_EVAL_LUA
, wxT("&Evaluate Lua statement"));
836 menu_entry(wxID_RUN_LUA
, wxT("R&un Lua script"));
839 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch"));
841 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch"));
842 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch"));
844 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search"));
845 //Settings menu: (ACFOS)
846 menu_start(wxT("Settings"));
847 menu_entry(wxID_EDIT_AXES
, wxT("Configure axes"));
848 menu_entry(wxID_EDIT_SETTINGS
, wxT("Configure settings"));
849 menu_entry(wxID_EDIT_KEYBINDINGS
, wxT("Configure keybindings"));
850 menu_entry(wxID_EDIT_ALIAS
, wxT("Configure aliases"));
851 menu_entry(wxID_EDIT_JUKEBOX
, wxT("Configure jukebox"));
852 if(platform::sound_initialized()) {
853 //Sound menu: (ACFOS)EHU
854 menu_start(wxT("S&ound"));
855 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("So&unds enabled"));
856 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
857 menu_entry(wxID_SHOW_AUDIO_STATUS
, wxT("S&how audio status"));
859 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
860 new sound_select_menu(this)));
861 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
865 void wxwin_mainwindow::request_paint()
870 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
872 //Veto it for now, latter things will delete it.
874 platform::queue("quit-emulator");
877 void wxwin_mainwindow::notify_update() throw()
879 if(!main_window_dirty
) {
880 main_window_dirty
= true;
885 void wxwin_mainwindow::notify_update_status() throw()
888 spanel
->dirty
= true;
893 void wxwin_mainwindow::notify_exit() throw()
895 join_emulator_thread();
899 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
902 wxTextEntryDialog
* d2
;
903 std::string filename
;
906 case wxID_FRAMEADVANCE
:
907 platform::queue("+advance-frame");
908 platform::queue("-advance-frame");
910 case wxID_SUBFRAMEADVANCE
:
911 platform::queue("+advance-poll");
912 platform::queue("-advance-poll");
915 platform::queue("advance-skiplag");
918 platform::queue("pause-emulator");
921 platform::queue("reset");
924 platform::queue("quit-emulator");
926 case wxID_AUDIO_ENABLED
:
927 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
929 case wxID_SHOW_AUDIO_STATUS
:
930 platform::queue("show-sound-status");
932 case wxID_LOAD_MOVIE
:
933 d
= new wxFileDialog(this, wxT("Load Movie"), wxT("."));
934 if(d
->ShowModal() == wxID_OK
)
935 filename
= tostdstring(d
->GetPath());
939 platform::queue("load-movie " + filename
);
941 case wxID_LOAD_STATE
:
942 d
= new wxFileDialog(this, wxT("Load State"), wxT("."));
943 if(d
->ShowModal() == wxID_OK
)
944 filename
= tostdstring(d
->GetPath());
948 platform::queue("load " + filename
);
950 case wxID_LOAD_STATE_RO
:
951 d
= new wxFileDialog(this, wxT("Load State (Read-Only)"), wxT("."));
952 if(d
->ShowModal() == wxID_OK
)
953 filename
= tostdstring(d
->GetPath());
957 platform::queue("load-readonly " + filename
);
959 case wxID_LOAD_STATE_RW
:
960 d
= new wxFileDialog(this, wxT("Load State (Read-Write)"), wxT("."));
961 if(d
->ShowModal() == wxID_OK
)
962 filename
= tostdstring(d
->GetPath());
966 platform::queue("load-state " + filename
);
968 case wxID_LOAD_STATE_P
:
969 d
= new wxFileDialog(this, wxT("Load State (Preserve)"), wxT("."));
970 if(d
->ShowModal() == wxID_OK
)
971 filename
= tostdstring(d
->GetPath());
975 platform::queue("load-preserve " + filename
);
977 case wxID_REWIND_MOVIE
:
978 platform::queue("rewind-movie");
980 case wxID_SAVE_MOVIE
:
981 d
= new wxFileDialog(this, wxT("Save Movie"), wxT("."));
982 if(d
->ShowModal() == wxID_OK
)
983 filename
= tostdstring(d
->GetPath());
987 platform::queue("save-movie " + filename
);
989 case wxID_SAVE_STATE
:
990 d
= new wxFileDialog(this, wxT("Save State"), wxT("."));
991 if(d
->ShowModal() == wxID_OK
)
992 filename
= tostdstring(d
->GetPath());
996 platform::queue("save-state " + filename
);
998 case wxID_SAVE_SCREENSHOT
:
999 d
= new wxFileDialog(this, wxT("Save Screenshot"), wxT("."));
1000 if(d
->ShowModal() == wxID_OK
)
1001 filename
= tostdstring(d
->GetPath());
1005 platform::queue("take-screenshot " + filename
);
1007 case wxID_RUN_SCRIPT
:
1008 d
= new wxFileDialog(this, wxT("Select Script"), wxT("."));
1009 if(d
->ShowModal() == wxID_OK
)
1010 filename
= tostdstring(d
->GetPath());
1014 platform::queue("run-script " + filename
);
1017 d
= new wxFileDialog(this, wxT("Select Lua Script"), wxT("."));
1018 if(d
->ShowModal() == wxID_OK
)
1019 filename
= tostdstring(d
->GetPath());
1023 platform::queue("run-lua " + filename
);
1026 d2
= new wxTextEntryDialog(this, wxT("Enter Lua statement:"), wxT("Evaluate Lua"));
1027 if(d2
->ShowModal() == wxID_OK
)
1028 filename
= tostdstring(d2
->GetValue());
1030 platform::queue("evaluate-lua " + filename
);
1032 case wxID_READONLY_MODE
:
1033 s
= menu_ischecked(wxID_READONLY_MODE
);
1034 platform::queue(_set_readonly
, &s
, true);
1036 case wxID_EDIT_AXES
:
1037 wxeditor_axes_display(this);
1039 case wxID_EDIT_AUTHORS
:
1040 wxeditor_authors_display(this);
1042 case wxID_EDIT_SETTINGS
:
1043 wxeditor_settings_display(this);
1045 case wxID_EDIT_KEYBINDINGS
:
1046 menu_edit_keybindings(e
);
1048 case wxID_EDIT_ALIAS
:
1049 menu_edit_aliases(e
);
1051 case wxID_EDIT_JUKEBOX
:
1052 menu_edit_jukebox(e
);
1054 case wxID_EDIT_MEMORYWATCH
:
1055 menu_edit_memorywatch(e
);
1057 case wxID_SAVE_MEMORYWATCH
:
1058 menu_save_memorywatch(e
);
1060 case wxID_LOAD_MEMORYWATCH
:
1061 menu_load_memorywatch(e
);
1063 case wxID_MEMORY_SEARCH
:
1064 wxwindow_memorysearch_display();
1067 std::ostringstream str
;
1068 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1069 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1070 str
<< "Core: " << bsnes_core_version
<< std::endl
;
1071 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1077 #define NEW_KEYBINDING "A new binding..."
1078 #define NEW_ALIAS "A new alias..."
1079 #define NEW_WATCH "A new watch..."
1081 void wxwin_mainwindow::menu_edit_keybindings(wxCommandEvent
& e
)
1083 platform::set_modal_pause(true);
1084 std::set
<std::string
> bind
;
1085 runemufn([&bind
]() { bind
= keymapper::get_bindings(); });
1086 std::vector
<wxString
> choices
;
1087 choices
.push_back(wxT(NEW_KEYBINDING
));
1089 choices
.push_back(towxstring(i
));
1090 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1091 wxT("Select binding"), choices
.size(), &choices
[0]);
1092 if(d
->ShowModal() == wxID_CANCEL
) {
1094 platform::set_modal_pause(false);
1097 std::string key
= tostdstring(d
->GetStringSelection());
1099 if(key
== NEW_KEYBINDING
) {
1100 wxdialog_keyentry
* d2
= new wxdialog_keyentry(this);
1101 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1102 // wxT("Edit binding"), wxT(""));
1103 if(d2
->ShowModal() == wxID_CANCEL
) {
1105 platform::set_modal_pause(false);
1109 //key = tostdstring(d2->GetValue());
1112 std::string old_command_value
;
1113 runemufn([&old_command_value
, key
]() { old_command_value
= keymapper::get_command_for(key
); });
1114 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1115 towxstring(old_command_value
));
1116 if(d4
->ShowModal() == wxID_CANCEL
) {
1118 platform::set_modal_pause(false);
1122 std::string faulttext
;
1123 std::string newcommand
= tostdstring(d4
->GetValue());
1124 runemufn([&fault
, &faulttext
, key
, newcommand
]() {
1126 keymapper::bind_for(key
, newcommand
);
1127 } catch(std::exception
& e
) {
1131 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1132 faulttext
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1137 platform::set_modal_pause(false);
1140 void strip_CR(std::string
& x
) throw(std::bad_alloc
);
1142 void wxwin_mainwindow::menu_edit_aliases(wxCommandEvent
& e
)
1144 platform::set_modal_pause(true);
1145 std::set
<std::string
> bind
;
1146 runemufn([&bind
]() { bind
= command::get_aliases(); });
1147 std::vector
<wxString
> choices
;
1148 choices
.push_back(wxT(NEW_ALIAS
));
1150 choices
.push_back(towxstring(i
));
1151 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1152 wxT("Select alias"), choices
.size(), &choices
[0]);
1153 if(d
->ShowModal() == wxID_CANCEL
) {
1155 platform::set_modal_pause(false);
1158 std::string alias
= tostdstring(d
->GetStringSelection());
1160 if(alias
== NEW_ALIAS
) {
1161 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1162 wxT("Enter alias name"));
1163 if(d2
->ShowModal() == wxID_CANCEL
) {
1165 platform::set_modal_pause(false);
1168 alias
= tostdstring(d2
->GetValue());
1170 if(!command::valid_alias_name(alias
)) {
1171 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1172 "name: ") + alias
), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1175 platform::set_modal_pause(false);
1179 std::string old_alias_value
= command::get_alias_for(alias
);
1180 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1181 towxstring(old_alias_value
), wxOK
| wxCANCEL
| wxCENTRE
| wxTE_MULTILINE
);
1182 if(d4
->ShowModal() == wxID_CANCEL
) {
1184 platform::set_modal_pause(false);
1187 std::string newcmd
= tostdstring(d4
->GetValue());
1188 runemufn([alias
, newcmd
]() { command::set_alias_for(alias
, newcmd
); });
1190 platform::set_modal_pause(false);
1193 void wxwin_mainwindow::menu_edit_jukebox(wxCommandEvent
& e
)
1195 platform::set_modal_pause(true);
1197 std::vector
<std::string
> new_jukebox
;
1199 for(auto i
: get_jukebox_names())
1203 wxTextEntryDialog
* dialog
= new wxTextEntryDialog(this, wxT("List jukebox entries"), wxT("Configure jukebox"),
1204 towxstring(x
), wxOK
| wxCANCEL
| wxCENTRE
| wxTE_MULTILINE
);
1205 if(dialog
->ShowModal() == wxID_CANCEL
) {
1207 platform::set_modal_pause(false);
1210 x
= tostdstring(dialog
->GetValue());
1214 size_t split
= x
.find_first_of("\n");
1216 if(split
< x
.length()) {
1217 l
= x
.substr(0, split
);
1218 x
= x
.substr(split
+ 1);
1225 new_jukebox
.push_back(l
);
1227 runemufn([&new_jukebox
]() { set_jukebox_names(new_jukebox
); });
1228 notify_update_status();
1229 platform::set_modal_pause(false);
1232 void wxwin_mainwindow::menu_load_memorywatch(wxCommandEvent
& e
)
1234 platform::set_modal_pause(true);
1235 std::set
<std::string
> old_watches
;
1236 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1237 std::map
<std::string
, std::string
> new_watches
;
1238 std::string filename
;
1240 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1241 if(d
->ShowModal() == wxID_CANCEL
) {
1243 platform::set_modal_pause(false);
1246 filename
= tostdstring(d
->GetPath());
1248 //Did we pick a .zip file?
1250 zip_reader
zr(filename
);
1251 std::vector
<wxString
> files
;
1253 files
.push_back(towxstring(i
));
1254 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1255 wxT("Select member"), files
.size(), &files
[0]);
1256 if(d2
->ShowModal() == wxID_CANCEL
) {
1258 platform::set_modal_pause(false);
1261 filename
= filename
+ "/" + tostdstring(d2
->GetStringSelection());
1268 std::istream
& in
= open_file_relative(filename
, "");
1272 std::getline(in
, wname
);
1273 std::getline(in
, wexpr
);
1274 new_watches
[wname
] = wexpr
;
1277 } catch(std::exception
& e
) {
1278 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1279 "watch: ") + e
.what()), wxT("Error"), wxOK
| wxICON_EXCLAMATION
);
1282 platform::set_modal_pause(false);
1286 runemufn([&new_watches
, &old_watches
]() {
1287 for(auto i
: new_watches
)
1288 set_watchexpr_for(i
.first
, i
.second
);
1289 for(auto i
: old_watches
)
1290 if(!new_watches
.count(i
))
1291 set_watchexpr_for(i
, "");
1293 platform::set_modal_pause(false);
1296 void wxwin_mainwindow::menu_save_memorywatch(wxCommandEvent
& e
)
1298 platform::set_modal_pause(true);
1299 std::set
<std::string
> old_watches
;
1300 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1301 std::string filename
;
1303 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1304 if(d
->ShowModal() == wxID_CANCEL
) {
1306 platform::set_modal_pause(false);
1309 filename
= tostdstring(d
->GetPath());
1312 std::ofstream
out(filename
.c_str());
1313 for(auto i
: old_watches
)
1314 out
<< i
<< std::endl
<< get_watchexpr_for(i
) << std::endl
;
1316 platform::set_modal_pause(false);
1320 void wxwin_mainwindow::menu_edit_memorywatch(wxCommandEvent
& e
)
1322 platform::set_modal_pause(true);
1323 std::set
<std::string
> bind
;
1324 runemufn([&bind
]() { bind
= get_watches(); });
1325 std::vector
<wxString
> choices
;
1326 choices
.push_back(wxT(NEW_WATCH
));
1328 choices
.push_back(towxstring(i
));
1329 wxSingleChoiceDialog
* d
= new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1330 wxT("Select watch"), choices
.size(), &choices
[0]);
1331 if(d
->ShowModal() == wxID_CANCEL
) {
1333 platform::set_modal_pause(false);
1336 std::string watch
= tostdstring(d
->GetStringSelection());
1338 if(watch
== NEW_WATCH
) {
1339 wxTextEntryDialog
* d2
= new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1340 wxT("Enter watch name"));
1341 if(d2
->ShowModal() == wxID_CANCEL
) {
1343 platform::set_modal_pause(false);
1346 watch
= tostdstring(d2
->GetValue());
1349 std::string old_watch_value
= get_watchexpr_for(watch
);
1350 wxTextEntryDialog
* d4
= new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1351 towxstring(old_watch_value
), wxOK
| wxCANCEL
| wxCENTRE
);
1352 if(d4
->ShowModal() == wxID_CANCEL
) {
1354 platform::set_modal_pause(false);
1357 std::string newexpr
= tostdstring(d4
->GetValue());
1358 runemufn([watch
, newexpr
]() { set_watchexpr_for(watch
, newexpr
); });
1359 platform::set_modal_pause(false);