3 #include "core/emucore.hpp"
5 #include "core/audioapi.hpp"
6 #include "core/command.hpp"
7 #include "core/controller.hpp"
8 #include "core/controllerframe.hpp"
9 #include "core/dispatch.hpp"
10 #include "core/framebuffer.hpp"
11 #include "core/framerate.hpp"
12 #include "core/loadlib.hpp"
13 #include "lua/lua.hpp"
14 #include "core/mainloop.hpp"
15 #include "core/memorywatch.hpp"
16 #include "core/misc.hpp"
17 #include "core/moviedata.hpp"
18 #include "core/settings.hpp"
19 #include "core/window.hpp"
20 #include "library/minmax.hpp"
21 #include "library/string.hpp"
22 #include "library/zip.hpp"
30 #include "platform/wxwidgets/menu_dump.hpp"
31 #include "platform/wxwidgets/platform.hpp"
32 #include "platform/wxwidgets/window_mainwindow.hpp"
33 #include "platform/wxwidgets/window_messages.hpp"
34 #include "platform/wxwidgets/window_status.hpp"
39 #define UINT64_C(val) val##ULL
41 #include <libswscale/swscale.h>
46 wxID_PAUSE
= wxID_HIGHEST
+ 1,
53 wxID_AUDIODEV_LAST
= wxID_AUDIODEV_FIRST
+ 255,
70 wxID_AUTOHOLD_LAST
= wxID_AUTOHOLD_FIRST
+ 1023,
71 wxID_EDIT_MEMORYWATCH
,
72 wxID_SAVE_MEMORYWATCH
,
73 wxID_LOAD_MEMORYWATCH
,
77 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
99 wxID_SETTINGS_HOTKEYS
,
100 wxID_RELOAD_ROM_IMAGE
,
104 wxID_DEDICATED_MEMORY_WATCH
,
106 wxID_RMOVIE_LAST
= wxID_RMOVIE_FIRST
+ 16,
108 wxID_RROM_LAST
= wxID_RROM_FIRST
+ 16,
113 double horizontal_scale_factor
= 1.0;
114 double vertical_scale_factor
= 1.0;
115 int scaling_flags
= SWS_POINT
;
116 bool hflip_enabled
= false;
117 bool vflip_enabled
= false;
118 bool rotate_enabled
= false;
122 std::string last_volume
= "0dB";
123 std::string last_volume_record
= "0dB";
124 std::string last_volume_voice
= "0dB";
125 unsigned char* screen_buffer
;
126 uint32_t* rotate_buffer
;
129 int old_flags
= SWS_POINT
;
130 bool old_hflip
= false;
131 bool old_vflip
= false;
132 bool old_rotate
= false;
133 bool main_window_dirty
;
134 struct thread
* emulation_thread
;
136 double pick_volume(wxWindow
* win
, const std::string
& title
, std::string
& last
)
141 value
= pick_text(win
, title
, "Enter volume in absolute units, percentage (%) or dB:",
143 if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)", value
))
144 parsed
= strtod(r
[1].c_str(), NULL
);
145 else if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)%", value
))
146 parsed
= strtod(r
[1].c_str(), NULL
) / 100;
147 else if(r
= regex("([+-]?([0-9]*.[0-9]+|[0-9]+))dB", value
))
148 parsed
= pow(10, strtod(r
[1].c_str(), NULL
) / 20);
150 wxMessageBox(wxT("Invalid volume"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, win
);
157 void recent_rom_selected(const std::string
& file
)
159 platform::queue("unpause-emulator");
160 platform::queue("reload-rom " + file
);
163 void recent_movie_selected(const std::string
& file
)
165 platform::queue("load-smart " + file
);
170 std::string windowname
= "lsnes rr" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
171 return towxstring(windowname
);
176 struct loaded_rom
* rom
;
177 struct moviefile
* initial
;
178 bool load_has_to_succeed
;
181 void* emulator_main(void* _args
)
183 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
186 struct moviefile
* movie
= args
->initial
;
187 bool has_to_succeed
= args
->load_has_to_succeed
;
188 platform::flush_command_queue();
189 main_loop(*our_rom
, *movie
, has_to_succeed
);
190 signal_program_exit();
191 } catch(std::bad_alloc
& e
) {
193 } catch(std::exception
& e
) {
194 messages
<< "FATAL: " << e
.what() << std::endl
;
195 platform::fatal_error();
200 void join_emulator_thread()
202 emulation_thread
->join();
205 keygroup
mouse_x("mouse_x", "mouse", keygroup::KT_MOUSE
);
206 keygroup
mouse_y("mouse_y", "mouse", keygroup::KT_MOUSE
);
207 keygroup
mouse_l("mouse_left", "mouse", keygroup::KT_KEY
);
208 keygroup
mouse_m("mouse_center", "mouse", keygroup::KT_KEY
);
209 keygroup
mouse_r("mouse_right", "mouse", keygroup::KT_KEY
);
210 keygroup
mouse_i("mouse_inwindow", "mouse", keygroup::KT_KEY
);
212 void handle_wx_mouse(wxMouseEvent
& e
)
214 platform::queue(keypress(modifier_set(), mouse_x
, e
.GetX() / horizontal_scale_factor
));
215 platform::queue(keypress(modifier_set(), mouse_y
, e
.GetY() / vertical_scale_factor
));
217 platform::queue(keypress(modifier_set(), mouse_i
, 1));
219 platform::queue(keypress(modifier_set(), mouse_i
, 0));
221 platform::queue(keypress(modifier_set(), mouse_l
, 1));
223 platform::queue(keypress(modifier_set(), mouse_l
, 0));
225 platform::queue(keypress(modifier_set(), mouse_m
, 1));
227 platform::queue(keypress(modifier_set(), mouse_m
, 0));
229 platform::queue(keypress(modifier_set(), mouse_r
, 1));
231 platform::queue(keypress(modifier_set(), mouse_r
, 0));
234 bool is_readonly_mode()
237 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
241 bool UI_get_autohold(unsigned pid
, unsigned idx
)
244 runemufn([&ret
, pid
, idx
]() { ret
= controls
.autohold(pid
, idx
); });
248 void UI_change_autohold(unsigned pid
, unsigned idx
, bool newstate
)
250 runemufn([pid
, idx
, newstate
]() { controls
.autohold(pid
, idx
, newstate
); });
253 int UI_controller_index_by_logical(unsigned lid
)
256 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
260 int UI_button_id(unsigned pcid
, unsigned lidx
)
263 runemufn([&ret
, pcid
, lidx
]() { ret
= controls
.button_id(pcid
, lidx
); });
267 void set_speed(double target
)
269 std::string v
= (stringfmt() << target
).str();
271 setting::set("targetfps", "infinite");
273 setting::set("targetfps", v
);
276 class controller_autohold_menu
: public wxMenu
279 controller_autohold_menu(unsigned lid
);
282 void on_select(wxCommandEvent
& e
);
283 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
287 std::vector
<wxMenuItem
*> entries
;
288 unsigned enabled_entries
;
289 std::map
<unsigned, int> pidxs
;
290 std::vector
<bool> autoholds
;
293 class autohold_menu
: public wxMenu
296 autohold_menu(wxwin_mainwindow
* win
);
298 void on_select(wxCommandEvent
& e
);
299 void update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
301 std::vector
<controller_autohold_menu
*> menus
;
302 std::vector
<wxMenuItem
*> entries
;
305 class sound_select_menu
: public wxMenu
308 sound_select_menu(wxwin_mainwindow
* win
);
309 void update(const std::string
& dev
);
310 void on_select(wxCommandEvent
& e
);
312 std::map
<std::string
, wxMenuItem
*> items
;
313 std::map
<int, std::string
> devices
;
316 class sound_select_menu
;
318 class broadcast_listener
: public information_dispatch
321 broadcast_listener(wxwin_mainwindow
* win
);
322 void set_sound_select(sound_select_menu
* sdev
);
323 void set_autohold_menu(autohold_menu
* ah
);
324 void on_sound_unmute(bool unmute
) throw();
325 void on_sound_change(const std::string
& dev
) throw();
326 void on_mode_change(bool readonly
) throw();
327 void on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
);
328 void on_autohold_reconfigure();
330 wxwin_mainwindow
* mainw
;
331 sound_select_menu
* sounddev
;
332 autohold_menu
* ahmenu
;
335 controller_autohold_menu::controller_autohold_menu(unsigned lid
)
337 auto limits
= get_core_logical_controller_limits();
338 entries
.resize(limits
.second
);
339 modal_pause_holder hld
;
341 for(unsigned i
= 0; i
< limits
.second
; i
++) {
342 int id
= wxID_AUTOHOLD_FIRST
+ limits
.second
* lid
+ i
;
343 entries
[i
] = AppendCheckItem(id
, towxstring(get_logical_button_name(i
)));
348 void controller_autohold_menu::change_type()
351 our_pid
= controls
.lcid_to_pcid(our_lid
);
352 auto limits
= get_core_logical_controller_limits();
353 this->autoholds
.resize(limits
.second
);
354 //We have asserted modal pause.
355 for(unsigned i
= 0; i
< limits
.second
; i
++) {
357 if(this->our_pid
>= 0)
358 this->pidxs
[i
] = controls
.button_id(this->our_pid
, i
);
359 if(this->pidxs
[i
] >= 0)
360 this->autoholds
[i
] = controls
.autohold(this->our_pid
, this->pidxs
[i
]);
362 this->autoholds
[i
] = false;
364 for(auto i
: pidxs
) {
366 entries
[i
.first
]->Check(autoholds
[i
.first
]);
367 entries
[i
.first
]->Enable();
370 entries
[i
.first
]->Check(false);
371 entries
[i
.first
]->Enable(false);
376 bool controller_autohold_menu::is_dummy()
378 return !enabled_entries
;
381 void controller_autohold_menu::on_select(wxCommandEvent
& e
)
383 auto limits
= get_core_logical_controller_limits();
385 if(x
< wxID_AUTOHOLD_FIRST
+ our_lid
* limits
.second
|| x
>= wxID_AUTOHOLD_FIRST
*
386 (our_lid
+ 1) * limits
.second
) {
389 unsigned lidx
= (x
- wxID_AUTOHOLD_FIRST
) % limits
.second
;
390 modal_pause_holder hld
;
391 int pid
= controls
.lcid_to_pcid(our_lid
);
392 if(pid
< 0 || !entries
[lidx
])
394 int pidx
= controls
.button_id(pid
, lidx
);
397 //Autohold change on pid=pid, ctrlindx=idx, state
398 bool newstate
= entries
[lidx
]->IsChecked();
399 UI_change_autohold(pid
, pidx
, newstate
);
402 void controller_autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
404 modal_pause_holder hld
;
405 if(our_pid
< 0 || static_cast<unsigned>(pid
) != our_pid
)
407 auto limits
= get_core_logical_controller_limits();
408 for(unsigned i
= 0; i
< limits
.second
; i
++) {
409 if(pidxs
[i
] < 0 || static_cast<unsigned>(pidxs
[i
]) != ctrlnum
)
411 entries
[i
]->Check(newstate
);
416 autohold_menu::autohold_menu(wxwin_mainwindow
* win
)
418 auto limits
= get_core_logical_controller_limits();
419 entries
.resize(limits
.first
);
420 menus
.resize(limits
.first
);
421 for(unsigned i
= 0; i
< limits
.first
; i
++) {
422 std::ostringstream str
;
423 str
<< "Controller #&" << (i
+ 1);
424 menus
[i
] = new controller_autohold_menu(i
);
425 entries
[i
] = AppendSubMenu(menus
[i
], towxstring(str
.str()));
426 entries
[i
]->Enable(!menus
[i
]->is_dummy());
428 win
->Connect(wxID_AUTOHOLD_FIRST
, wxID_AUTOHOLD_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
429 wxCommandEventHandler(autohold_menu::on_select
), NULL
, this);
433 void autohold_menu::reconfigure()
435 modal_pause_holder hld
;
436 auto limits
= get_core_logical_controller_limits();
437 for(unsigned i
= 0; i
< limits
.first
; i
++) {
438 menus
[i
]->change_type();
439 entries
[i
]->Enable(!menus
[i
]->is_dummy());
443 void autohold_menu::on_select(wxCommandEvent
& e
)
445 auto limits
= get_core_logical_controller_limits();
446 for(unsigned i
= 0; i
< limits
.first
; i
++)
447 menus
[i
]->on_select(e
);
450 void autohold_menu::update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
452 auto limits
= get_core_logical_controller_limits();
453 for(unsigned i
= 0; i
< limits
.first
; i
++)
454 menus
[i
]->update(pid
, ctrlnum
, newstate
);
457 sound_select_menu::sound_select_menu(wxwin_mainwindow
* win
)
459 std::string curdev
= audioapi_driver_get_device();
460 int j
= wxID_AUDIODEV_FIRST
;
461 for(auto i
: audioapi_driver_get_devices()) {
462 items
[i
.first
] = AppendRadioItem(j
, towxstring(i
.first
+ "(" + i
.second
+ ")"));
463 devices
[j
] = i
.first
;
464 if(i
.first
== curdev
)
465 items
[i
.first
]->Check();
466 win
->Connect(j
, wxEVT_COMMAND_MENU_SELECTED
,
467 wxCommandEventHandler(sound_select_menu::on_select
), NULL
, this);
472 void sound_select_menu::update(const std::string
& dev
)
477 void sound_select_menu::on_select(wxCommandEvent
& e
)
479 std::string devname
= devices
[e
.GetId()];
481 runemufn([devname
]() { platform::set_sound_device(devname
); });
484 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
485 : information_dispatch("wxwidgets-broadcast-listener")
490 void broadcast_listener::set_sound_select(sound_select_menu
* sdev
)
495 void broadcast_listener::set_autohold_menu(autohold_menu
* ah
)
500 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
502 runuifun([this, unmute
]() { this->mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
505 void broadcast_listener::on_sound_change(const std::string
& dev
) throw()
507 runuifun([this, dev
]() { if(this->sounddev
) this->sounddev
->update(dev
); });
510 void broadcast_listener::on_mode_change(bool readonly
) throw()
512 runuifun([this, readonly
]() { this->mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
515 void broadcast_listener::on_autohold_update(unsigned pid
, unsigned ctrlnum
, bool newstate
)
517 runuifun([this, pid
, ctrlnum
, newstate
]() { this->ahmenu
->update(pid
, ctrlnum
, newstate
); });
520 void broadcast_listener::on_autohold_reconfigure()
522 runuifun([this]() { this->ahmenu
->reconfigure(); });
525 path_setting
moviepath_setting("moviepath");
526 path_setting
rompath_setting("rompath");
528 std::string
movie_path()
530 return setting::get("moviepath");
533 std::string
rom_path()
535 return setting::get("rompath");
538 bool is_lsnes_movie(const std::string
& filename
)
541 zip_reader
r(filename
);
542 std::istream
& s
= r
["systemid"];
547 return (s2
== "lsnes-rr1");
553 class loadfile
: public wxFileDropTarget
556 loadfile(wxwin_mainwindow
* win
) : pwin(win
) {};
557 bool OnDropFiles(wxCoord x
, wxCoord y
, const wxArrayString
& filenames
)
560 if(filenames
.Count() == 2) {
561 if(is_lsnes_movie(tostdstring(filenames
[0])) &&
562 !is_lsnes_movie(tostdstring(filenames
[1]))) {
563 platform::queue("unpause-emulator");
564 platform::queue("reload-rom " + tostdstring(filenames
[1]));
565 platform::queue("load-smart " + tostdstring(filenames
[0]));
568 if(!is_lsnes_movie(tostdstring(filenames
[0])) &&
569 is_lsnes_movie(tostdstring(filenames
[1]))) {
570 platform::queue("unpause-emulator");
571 platform::queue("reload-rom " + tostdstring(filenames
[0]));
572 platform::queue("load-smart " + tostdstring(filenames
[1]));
576 if(filenames
.Count() == 1) {
577 if(is_lsnes_movie(tostdstring(filenames
[0]))) {
578 platform::queue("load-smart " + tostdstring(filenames
[0]));
579 pwin
->recent_movies
->add(tostdstring(filenames
[0]));
582 platform::queue("unpause-emulator");
583 platform::queue("reload-rom " + tostdstring(filenames
[0]));
584 pwin
->recent_roms
->add(tostdstring(filenames
[0]));
590 wxwin_mainwindow
* pwin
;
594 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
597 struct emu_args
* a
= new emu_args
;
600 a
->load_has_to_succeed
= false;
601 modal_pause_holder hld
;
602 emulation_thread
= &thread::create(emulator_main
, a
);
603 main_window
= new wxwin_mainwindow();
605 } catch(std::bad_alloc
& e
) {
610 wxwin_mainwindow::panel::panel(wxWindow
* win
)
611 : wxPanel(win
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
)
613 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
614 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
615 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
616 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
617 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
618 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
619 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
620 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
621 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
622 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
623 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
624 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
625 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
626 SetMinSize(wxSize(512, 448));
629 void wxwin_mainwindow::menu_start(wxString name
)
631 while(!upper
.empty())
633 current_menu
= new wxMenu();
634 menubar
->Append(current_menu
, name
);
637 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
639 while(!upper
.empty())
641 menubar
->Append(menu
, name
);
645 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
647 current_menu
->AppendSubMenu(menu
, name
);
650 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
652 current_menu
->Append(id
, name
);
653 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
654 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
657 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
659 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
660 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
661 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
664 void wxwin_mainwindow::menu_start_sub(wxString name
)
666 wxMenu
* old
= current_menu
;
667 upper
.push(current_menu
);
668 current_menu
= new wxMenu();
669 old
->AppendSubMenu(current_menu
, name
);
672 void wxwin_mainwindow::menu_end_sub()
674 current_menu
= upper
.top();
678 bool wxwin_mainwindow::menu_ischecked(int id
)
680 if(checkitems
.count(id
))
681 return checkitems
[id
]->IsChecked();
686 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
688 if(checkitems
.count(id
))
689 return checkitems
[id
]->Check(newstate
);
694 void wxwin_mainwindow::menu_separator()
696 current_menu
->AppendSeparator();
699 void wxwin_mainwindow::panel::request_paint()
704 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
706 render_framebuffer();
707 static struct SwsContext
* ctx
;
714 bool aux
= hflip_enabled
|| vflip_enabled
|| rotate_enabled
;
716 tw
= main_screen
.get_height() * horizontal_scale_factor
+ 0.5;
717 th
= main_screen
.get_width() * vertical_scale_factor
+ 0.5;
719 tw
= main_screen
.get_width() * horizontal_scale_factor
+ 0.5;
720 th
= main_screen
.get_height() * vertical_scale_factor
+ 0.5;
723 main_window_dirty
= false;
726 if(!screen_buffer
|| tw
!= old_width
|| th
!= old_height
|| scaling_flags
!= old_flags
||
727 hflip_enabled
!= old_hflip
|| vflip_enabled
!= old_vflip
|| rotate_enabled
!= old_rotate
) {
729 delete[] screen_buffer
;
731 delete[] rotate_buffer
;
734 old_flags
= scaling_flags
;
735 old_hflip
= hflip_enabled
;
736 old_vflip
= vflip_enabled
;
737 old_rotate
= rotate_enabled
;
738 uint32_t w
= main_screen
.get_width();
739 uint32_t h
= main_screen
.get_height();
741 ctx
= sws_getCachedContext(ctx
, rotate_enabled
? h
: w
, rotate_enabled
? w
: h
, PIX_FMT_RGBA
,
742 tw
, th
, PIX_FMT_BGR24
, scaling_flags
, NULL
, NULL
, NULL
);
743 tw
= max(tw
, static_cast<uint32_t>(128));
744 th
= max(th
, static_cast<uint32_t>(112));
745 screen_buffer
= new unsigned char[tw
* th
* 3];
747 rotate_buffer
= new uint32_t[main_screen
.get_width() * main_screen
.get_height()];
748 SetMinSize(wxSize(tw
, th
));
749 signal_resize_needed();
752 //Hflip, Vflip or rotate active.
753 size_t width
= main_screen
.get_width();
754 size_t height
= main_screen
.get_height();
755 size_t width1
= width
- 1;
756 size_t height1
= height
- 1;
757 size_t stride
= main_screen
.rowptr(1) - main_screen
.rowptr(0);
758 uint32_t* pixels
= main_screen
.rowptr(0);
760 for(unsigned y
= 0; y
< height
; y
++) {
761 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
762 uint32_t* dpixels
= rotate_buffer
+ (height1
- y
);
764 for(unsigned x
= 0; x
< width
; x
++)
765 dpixels
[x
* height
] = pixels2
[width1
- x
];
767 for(unsigned x
= 0; x
< width
; x
++)
768 dpixels
[x
* height
] = pixels2
[x
];
771 for(unsigned y
= 0; y
< height
; y
++) {
772 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
773 uint32_t* dpixels
= rotate_buffer
+ y
* width
;
775 for(unsigned x
= 0; x
< width
; x
++)
776 dpixels
[x
] = pixels2
[width1
- x
];
778 for(unsigned x
= 0; x
< width
; x
++)
779 dpixels
[x
] = pixels2
[x
];
783 srcs
[0] = 4 * (rotate_enabled
? main_screen
.get_height() : main_screen
.get_width());
785 srcp
[0] = reinterpret_cast<unsigned char*>(aux
? rotate_buffer
: main_screen
.rowptr(0));
786 dstp
[0] = screen_buffer
;
787 memset(screen_buffer
, 0, tw
* th
* 3);
788 if(main_screen
.get_width() && main_screen
.get_height())
789 sws_scale(ctx
, srcp
, srcs
, 0, rotate_enabled
? main_screen
.get_width() : main_screen
.get_height(),
791 wxBitmap
bmp(wxImage(tw
, th
, screen_buffer
, true));
792 dc
.DrawBitmap(bmp
, 0, 0, false);
793 main_window_dirty
= false;
796 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
801 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
803 handle_wx_keyboard(e
, true);
806 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
808 handle_wx_keyboard(e
, false);
811 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
816 wxwin_mainwindow::wxwin_mainwindow()
817 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
818 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
820 broadcast_listener
* blistener
= new broadcast_listener(this);
823 toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
824 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
825 toplevel
->Add(spanel
= new wxwin_status::panel(this, gpanel
, 20), 1, wxGROW
);
827 toplevel
->SetSizeHints(this);
831 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
832 menubar
= new wxMenuBar
;
835 menu_start(wxT("File"));
836 menu_start_sub(wxT("New"));
837 menu_entry(wxID_NEW_MOVIE
, wxT("Movie..."));
839 menu_start_sub(wxT("Load"));
840 menu_entry(wxID_LOAD_STATE
, wxT("State..."));
841 menu_entry(wxID_LOAD_STATE_RO
, wxT("State (readonly)..."));
842 menu_entry(wxID_LOAD_STATE_RW
, wxT("State (read-write)..."));
843 menu_entry(wxID_LOAD_STATE_P
, wxT("State (preserve input)..."));
844 menu_entry(wxID_LOAD_MOVIE
, wxT("Movie..."));
845 if(load_library_supported
) {
847 menu_entry(wxID_LOAD_LIBRARY
, towxstring(std::string("Load ") + library_is_called
));
850 menu_entry(wxID_RELOAD_ROM_IMAGE
, wxT("Reload ROM"));
851 menu_entry(wxID_LOAD_ROM_IMAGE
, wxT("ROM..."));
853 menu_special_sub(wxT("Recent ROMs"), recent_roms
= new recent_menu(this, wxID_RROM_FIRST
, wxID_RROM_LAST
,
854 get_config_path() + "/recent-roms.txt", recent_rom_selected
));
855 menu_special_sub(wxT("Recent Movies"), recent_movies
= new recent_menu(this, wxID_RMOVIE_FIRST
,
856 wxID_RMOVIE_LAST
, get_config_path() + "/recent-movies.txt", recent_movie_selected
));
858 menu_start_sub(wxT("Save"));
859 menu_entry(wxID_SAVE_STATE
, wxT("State..."));
860 menu_entry(wxID_SAVE_MOVIE
, wxT("Movie..."));
861 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Screenshot..."));
862 menu_entry(wxID_SAVE_SUBTITLES
, wxT("Subtitles..."));
863 menu_entry(wxID_CANCEL_SAVES
, wxT("Cancel pending saves"));
866 menu_entry(wxID_EXIT
, wxT("Quit"));
868 menu_start(wxT("System"));
869 menu_entry(wxID_PAUSE
, wxT("Pause/Unpause"));
870 menu_entry(wxID_FRAMEADVANCE
, wxT("Step frame"));
871 menu_entry(wxID_SUBFRAMEADVANCE
, wxT("Step subframe"));
872 menu_entry(wxID_NEXTPOLL
, wxT("Step poll"));
873 menu_entry(wxID_ERESET
, wxT("Reset"));
875 menu_start(wxT("Movie"));
876 menu_entry_check(wxID_READONLY_MODE
, wxT("Readonly mode"));
877 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
878 menu_entry(wxID_EDIT_AUTHORS
, wxT("Edit game name && authors..."));
879 menu_entry(wxID_EDIT_SUBTITLES
, wxT("Edit subtitles..."));
880 #ifdef WITH_OPUS_CODEC
881 menu_entry(wxID_EDIT_VSUBTITLES
, wxT("Edit commantary track..."));
884 menu_entry(wxID_REWIND_MOVIE
, wxT("Rewind to start"));
886 //Autohold menu: (ACOS)
887 menu_special(wxT("Autohold"), reinterpret_cast<autohold_menu
*>(ahmenu
= new autohold_menu(this)));
888 blistener
->set_autohold_menu(reinterpret_cast<autohold_menu
*>(ahmenu
));
890 menu_start(wxT("Speed"));
891 menu_entry(wxID_SPEED_5
, wxT("1/20x"));
892 menu_entry(wxID_SPEED_10
, wxT("1/10x"));
893 menu_entry(wxID_SPEED_17
, wxT("1/6x"));
894 menu_entry(wxID_SPEED_20
, wxT("1/5x"));
895 menu_entry(wxID_SPEED_25
, wxT("1/4x"));
896 menu_entry(wxID_SPEED_33
, wxT("1/3x"));
897 menu_entry(wxID_SPEED_50
, wxT("1/2x"));
898 menu_entry(wxID_SPEED_100
, wxT("1x"));
899 menu_entry(wxID_SPEED_150
, wxT("1.5x"));
900 menu_entry(wxID_SPEED_200
, wxT("2x"));
901 menu_entry(wxID_SPEED_300
, wxT("3x"));
902 menu_entry(wxID_SPEED_500
, wxT("5x"));
903 menu_entry(wxID_SPEED_1000
, wxT("10x"));
904 menu_entry(wxID_SPEED_TURBO
, wxT("Turbo"));
905 menu_entry(wxID_SET_SPEED
, wxT("Set..."));
907 menu_start(wxT("Tools"));
908 menu_entry(wxID_RUN_SCRIPT
, wxT("Run batch file..."));
911 menu_entry(wxID_EVAL_LUA
, wxT("Evaluate Lua statement..."));
912 menu_entry(wxID_RUN_LUA
, wxT("Run Lua script..."));
914 menu_entry(wxID_RESET_LUA
, wxT("Reset Lua VM"));
917 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch..."));
919 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch..."));
920 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch..."));
922 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search..."));
924 menu_special_sub(wxT("Video Capture"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
925 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
927 menu_start(wxT("Configure"));
928 menu_entry_check(wxID_SHOW_STATUS
, wxT("Show status panel"));
929 menu_check(wxID_SHOW_STATUS
, true);
930 menu_entry_check(wxID_DEDICATED_MEMORY_WATCH
, wxT("Dedicated memory watch"));
931 menu_entry(wxID_SHOW_MESSAGES
, wxT("Show messages"));
932 menu_entry(wxID_SETTINGS
, wxT("Configure emulator..."));
933 menu_entry(wxID_SETTINGS_HOTKEYS
, wxT("Configure hotkeys..."));
934 if(audioapi_driver_initialized()) {
936 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("Sounds enabled"));
937 menu_entry(wxID_VUDISPLAY
, wxT("VU display / volume controls"));
938 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
939 menu_special_sub(wxT("Select sound device"), reinterpret_cast<sound_select_menu
*>(sounddev
=
940 new sound_select_menu(this)));
941 blistener
->set_sound_select(reinterpret_cast<sound_select_menu
*>(sounddev
));
944 menu_start(wxT("Help"));
945 menu_entry(wxID_ABOUT
, wxT("About..."));
947 gpanel
->SetDropTarget(new loadfile(this));
948 spanel
->SetDropTarget(new loadfile(this));
951 void wxwin_mainwindow::request_paint()
956 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
958 //Veto it for now, latter things will delete it.
960 platform::queue("quit-emulator");
963 void wxwin_mainwindow::notify_update() throw()
965 if(!main_window_dirty
) {
966 main_window_dirty
= true;
971 void wxwin_mainwindow::notify_resized() throw()
974 toplevel
->SetSizeHints(this);
978 void wxwin_mainwindow::notify_update_status() throw()
981 spanel
->dirty
= true;
985 mwindow
->notify_update();
988 void wxwin_mainwindow::notify_exit() throw()
990 wxwidgets_exiting
= true;
991 join_emulator_thread();
995 #define NEW_KEYBINDING "A new binding..."
996 #define NEW_ALIAS "A new alias..."
997 #define NEW_WATCH "A new watch..."
999 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
1002 handle_menu_click_cancelable(e
);
1003 } catch(canceled_exception
& e
) {
1005 } catch(std::bad_alloc
& e
) {
1007 } catch(std::exception
& e
) {
1008 show_message_ok(this, "Error in menu handler", e
.what(), wxICON_EXCLAMATION
);
1012 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent
& e
)
1014 std::string filename
;
1018 case wxID_FRAMEADVANCE
:
1019 platform::queue("+advance-frame");
1020 platform::queue("-advance-frame");
1022 case wxID_SUBFRAMEADVANCE
:
1023 platform::queue("+advance-poll");
1024 platform::queue("-advance-poll");
1027 platform::queue("advance-skiplag");
1030 platform::queue("pause-emulator");
1033 platform::queue("reset");
1036 platform::queue("quit-emulator");
1038 case wxID_AUDIO_ENABLED
:
1039 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
1041 case wxID_CANCEL_SAVES
:
1042 platform::queue("cancel-saves");
1044 case wxID_LOAD_MOVIE
:
1045 filename
= pick_file(this, "Load Movie", movie_path(), false);
1046 recent_movies
->add(filename
);
1047 platform::queue("load-movie " + filename
);
1049 case wxID_LOAD_STATE
:
1050 filename
= pick_file(this, "Load State", movie_path(), false);
1051 recent_movies
->add(filename
);
1052 platform::queue("load " + filename
);
1054 case wxID_LOAD_STATE_RO
:
1055 filename
= pick_file(this, "Load State (Read-Only)", movie_path(), false);
1056 recent_movies
->add(filename
);
1057 platform::queue("load-readonly " + filename
);
1059 case wxID_LOAD_STATE_RW
:
1060 filename
= pick_file(this, "Load State (Read-Write)", movie_path(), false);
1061 recent_movies
->add(filename
);
1062 platform::queue("load-state " + filename
);
1064 case wxID_LOAD_STATE_P
:
1065 filename
= pick_file(this, "Load State (Preserve)", movie_path(), false);
1066 recent_movies
->add(filename
);
1067 platform::queue("load-preserve " + filename
);
1069 case wxID_REWIND_MOVIE
:
1070 platform::queue("rewind-movie");
1072 case wxID_SAVE_MOVIE
:
1073 filename
= pick_file(this, "Save Movie", movie_path(), true);
1074 recent_movies
->add(filename
);
1075 platform::queue("save-movie " + filename
);
1077 case wxID_SAVE_SUBTITLES
:
1078 platform::queue("save-subtitle " + pick_file(this, "Save Subtitle (.sub)", movie_path(), true));
1080 case wxID_SAVE_STATE
:
1081 filename
= pick_file(this, "Save State", movie_path(), true);
1082 recent_movies
->add(filename
);
1083 platform::queue("save-state " + filename
);
1085 case wxID_SAVE_SCREENSHOT
:
1086 platform::queue("take-screenshot " + pick_file(this, "Save Screenshot", movie_path(), true));
1088 case wxID_RUN_SCRIPT
:
1089 platform::queue("run-script " + pick_file_member(this, "Select Script", "."));
1092 platform::queue("run-lua " + pick_file(this, "Select Lua Script", ".", false));
1094 case wxID_RESET_LUA
:
1095 platform::queue("reset-lua");
1098 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
1100 case wxID_READONLY_MODE
:
1101 s
= menu_ischecked(wxID_READONLY_MODE
);
1103 movb
.get_movie().readonly_mode(s
);
1105 lua_callback_do_readwrite();
1106 update_movie_state();
1109 case wxID_EDIT_AUTHORS
:
1110 wxeditor_authors_display(this);
1112 case wxID_EDIT_SUBTITLES
:
1113 wxeditor_subtitles_display(this);
1115 #ifdef WITH_OPUS_CODEC
1116 case wxID_EDIT_VSUBTITLES
:
1117 show_wxeditor_voicesub(this);
1120 case wxID_EDIT_MEMORYWATCH
:
1121 wxeditor_memorywatch_display(this);
1123 case wxID_SAVE_MEMORYWATCH
: {
1124 modal_pause_holder hld
;
1125 std::set
<std::string
> old_watches
;
1126 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1127 std::string filename
= pick_file(this, "Save watches to file", ".", true);
1128 std::ofstream
out(filename
.c_str());
1129 for(auto i
: old_watches
) {
1131 runemufn([i
, &val
]() { val
= get_watchexpr_for(i
); });
1132 out
<< i
<< std::endl
<< val
<< std::endl
;
1137 case wxID_LOAD_MEMORYWATCH
: {
1138 modal_pause_holder hld
;
1139 std::set
<std::string
> old_watches
;
1140 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1141 std::map
<std::string
, std::string
> new_watches
;
1142 std::string filename
= pick_file_member(this, "Choose memory watch file", ".");
1145 std::istream
& in
= open_file_relative(filename
, "");
1149 std::getline(in
, wname
);
1150 std::getline(in
, wexpr
);
1151 new_watches
[strip_CR(wname
)] = strip_CR(wexpr
);
1154 } catch(std::exception
& e
) {
1155 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e
.what(),
1156 wxICON_EXCLAMATION
);
1160 runemufn([&new_watches
, &old_watches
]() {
1161 for(auto i
: new_watches
)
1162 set_watchexpr_for(i
.first
, i
.second
);
1163 for(auto i
: old_watches
)
1164 if(!new_watches
.count(i
))
1165 set_watchexpr_for(i
, "");
1169 case wxID_MEMORY_SEARCH
:
1170 wxwindow_memorysearch_display();
1173 std::ostringstream str
;
1174 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1175 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1176 str
<< "Core: " << bsnes_core_version
<< std::endl
;
1177 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1180 case wxID_SHOW_STATUS
: {
1181 bool newstate
= menu_ischecked(wxID_SHOW_STATUS
);
1184 if(newstate
&& !spanel_shown
)
1185 toplevel
->Add(spanel
, 1, wxGROW
);
1186 else if(!newstate
&& spanel_shown
)
1187 toplevel
->Detach(spanel
);
1190 spanel_shown
= newstate
;
1192 toplevel
->SetSizeHints(this);
1196 case wxID_DEDICATED_MEMORY_WATCH
: {
1197 bool newstate
= menu_ischecked(wxID_DEDICATED_MEMORY_WATCH
);
1198 if(newstate
&& !mwindow
) {
1199 mwindow
= new wxwin_status(-1, "Memory Watch");
1200 spanel
->set_watch_flag(1);
1202 } else if(!newstate
&& mwindow
) {
1205 spanel
->set_watch_flag(0);
1209 case wxID_SET_SPEED
: {
1211 std::string value
= setting::is_set("targetfps") ? setting::get("targetfps") : "";
1212 value
= pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value
);
1214 setting::set("targetfps", value
);
1216 wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, this);
1227 set_speed(16.66666666666);
1236 set_speed(33.3333333333333);
1241 case wxID_SPEED_100
:
1244 case wxID_SPEED_150
:
1247 case wxID_SPEED_200
:
1250 case wxID_SPEED_300
:
1253 case wxID_SPEED_500
:
1256 case wxID_SPEED_1000
:
1259 case wxID_SPEED_TURBO
:
1262 case wxID_LOAD_LIBRARY
: {
1263 std::string name
= std::string("load ") + library_is_called
;
1264 load_library(pick_file(this, name
, ".", false));
1268 wxsetingsdialog_display(this, false);
1270 case wxID_SETTINGS_HOTKEYS
:
1271 wxsetingsdialog_display(this, true);
1273 case wxID_LOAD_ROM_IMAGE
:
1274 filename
= pick_file_member(this, "Select new ROM image", rom_path());
1275 recent_roms
->add(filename
);
1276 platform::queue("unpause-emulator");
1277 platform::queue("reload-rom " + filename
);
1279 case wxID_RELOAD_ROM_IMAGE
:
1280 platform::queue("unpause-emulator");
1281 platform::queue("reload-rom");
1283 case wxID_NEW_MOVIE
:
1284 show_projectwindow(this);
1286 case wxID_SHOW_MESSAGES
:
1287 msg_window
->reshow();
1289 case wxID_VUDISPLAY
:
1290 open_vumeter_window(this);