4 #include "platform/wxwidgets/menu_dump.hpp"
5 #include "platform/wxwidgets/platform.hpp"
6 #include "platform/wxwidgets/window_mainwindow.hpp"
7 #include "platform/wxwidgets/window_messages.hpp"
8 #include "platform/wxwidgets/window_status.hpp"
9 #include "platform/wxwidgets/window-romload.hpp"
11 #include "core/audioapi.hpp"
12 #include "core/command.hpp"
13 #include "core/controller.hpp"
14 #include "core/controllerframe.hpp"
15 #include "core/dispatch.hpp"
16 #include "core/framebuffer.hpp"
17 #include "core/framerate.hpp"
18 #include "interface/romtype.hpp"
19 #include "core/loadlib.hpp"
20 #include "lua/lua.hpp"
21 #include "core/mainloop.hpp"
22 #include "core/memorywatch.hpp"
23 #include "core/misc.hpp"
24 #include "core/moviedata.hpp"
25 #include "core/project.hpp"
26 #include "core/settings.hpp"
27 #include "core/window.hpp"
28 #include "library/directory.hpp"
29 #include "library/minmax.hpp"
30 #include "library/string.hpp"
31 #include "library/zip.hpp"
32 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
44 #define UINT64_C(val) val##ULL
46 #include <libswscale/swscale.h>
51 wxID_PAUSE
= wxID_HIGHEST
+ 1,
73 wxID_EDIT_MEMORYWATCH
,
74 wxID_SAVE_MEMORYWATCH
,
75 wxID_LOAD_MEMORYWATCH
,
79 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
101 wxID_SETTINGS_HOTKEYS
,
102 wxID_SETTINGS_CONTROLLERS
,
103 wxID_RELOAD_ROM_IMAGE
,
107 wxID_DEDICATED_MEMORY_WATCH
,
109 wxID_RMOVIE_LAST
= wxID_RMOVIE_FIRST
+ 16,
111 wxID_RROM_LAST
= wxID_RROM_FIRST
+ 16,
112 wxID_CONFLICTRESOLUTION
,
122 wxID_ACTIONS_LAST
= wxID_ACTIONS_FIRST
+ 256,
126 double horizontal_scale_factor
= 1.0;
127 double vertical_scale_factor
= 1.0;
128 int scaling_flags
= SWS_POINT
;
129 bool hflip_enabled
= false;
130 bool vflip_enabled
= false;
131 bool rotate_enabled
= false;
135 std::string last_volume
= "0dB";
136 std::string last_volume_record
= "0dB";
137 std::string last_volume_voice
= "0dB";
138 unsigned char* screen_buffer
;
139 uint32_t* rotate_buffer
;
142 int old_flags
= SWS_POINT
;
143 bool old_hflip
= false;
144 bool old_vflip
= false;
145 bool old_rotate
= false;
146 bool main_window_dirty
;
147 thread_class
* emulation_thread
;
149 std::pair
<std::string
, std::string
> lsplit(std::string l
)
151 for(unsigned i
= 0; i
< l
.length() - 3; i
++)
152 if((uint8_t)l
[i
] == 0xE2 && (uint8_t)l
[i
+ 1] == 0x80 && (uint8_t)l
[i
+ 2] == 0xA3)
153 return std::make_pair(l
.substr(0, i
), l
.substr(i
+ 3));
154 return std::make_pair("", l
);
157 class system_menu
: public wxMenu
160 system_menu(wxWindow
* win
);
162 void on_select(wxCommandEvent
& e
);
163 void update(bool light
);
166 void insert_pass(int id
, const std::string
& label
);
167 void insert_act(unsigned id
, const std::string
& label
, bool dots
, bool check
);
168 wxMenu
* lookup_menu(const std::string
& key
);
170 std::map
<int, unsigned> action_by_id
;
171 std::map
<unsigned, wxMenuItem
*> item_by_action
;
172 std::map
<wxMenuItem
*, wxMenu
*> menu_by_item
;
173 std::map
<std::string
, wxMenu
*> submenu_by_name
;
174 std::map
<std::string
, wxMenuItem
*> submenui_by_name
;
175 std::set
<unsigned> toggles
;
179 wxMenu
* system_menu::lookup_menu(const std::string
& key
)
183 if(submenu_by_name
.count(key
))
184 return submenu_by_name
[key
];
187 sep
= AppendSeparator();
188 auto p
= lsplit(key
);
189 wxMenu
* into
= lookup_menu(p
.first
);
190 submenu_by_name
[key
] = new wxMenu();
191 submenui_by_name
[key
] = into
->AppendSubMenu(submenu_by_name
[key
], towxstring(p
.second
));
192 menu_by_item
[submenui_by_name
[key
]] = into
;
193 return submenu_by_name
[key
];
196 void system_menu::insert_act(unsigned id
, const std::string
& label
, bool dots
, bool check
)
199 sep
= AppendSeparator();
201 auto p
= lsplit(label
);
202 wxMenu
* into
= lookup_menu(p
.first
);
204 action_by_id
[next_id
] = id
;
205 std::string use_label
= p
.second
+ (dots
? "..." : "");
207 item_by_action
[id
] = into
->AppendCheckItem(next_id
, towxstring(use_label
));
210 item_by_action
[id
] = into
->Append(next_id
, towxstring(use_label
));
211 menu_by_item
[item_by_action
[id
]] = into
;
212 pwin
->Connect(next_id
++, wxEVT_COMMAND_MENU_SELECTED
,
213 wxCommandEventHandler(system_menu::on_select
), NULL
, this);
216 void system_menu::insert_pass(int id
, const std::string
& label
)
218 pwin
->Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
219 wxCommandEventHandler(wxwin_mainwindow::handle_menu_click
), NULL
, pwin
);
220 Append(id
, towxstring(label
));
223 system_menu::system_menu(wxWindow
* win
)
226 insert_pass(wxID_PAUSE
, "Pause/Unpause");
227 insert_pass(wxID_FRAMEADVANCE
, "Step frame");
228 insert_pass(wxID_SUBFRAMEADVANCE
, "Step subframe");
229 insert_pass(wxID_NEXTPOLL
, "Step poll");
233 system_menu::~system_menu()
237 void system_menu::on_select(wxCommandEvent
& e
)
239 if(!action_by_id
.count(e
.GetId()))
241 unsigned act_id
= action_by_id
[e
.GetId()];
242 const interface_action
* act
= NULL
;
243 for(auto i
: our_rom
->rtype
->get_actions())
244 if(i
->id
== act_id
) {
251 auto p
= prompt_action_params(pwin
, act
->get_title(), act
->params
);
252 runemufn([act_id
,p
]() { our_rom
->rtype
->execute_action(act_id
, p
); });
253 } catch(canceled_exception
& e
) {
254 } catch(std::bad_alloc
& e
) {
259 void system_menu::update(bool light
)
262 next_id
= wxID_ACTIONS_FIRST
;
267 for(auto i
= item_by_action
.begin(); i
!= item_by_action
.end(); i
++)
268 menu_by_item
[i
->second
]->Destroy(i
->second
);
269 for(auto i
= submenui_by_name
.rbegin(); i
!= submenui_by_name
.rend(); i
++)
270 menu_by_item
[i
->second
]->Destroy(i
->second
);
271 action_by_id
.clear();
272 item_by_action
.clear();
273 menu_by_item
.clear();
274 submenu_by_name
.clear();
275 submenui_by_name
.clear();
278 for(auto i
: our_rom
->rtype
->get_actions())
279 insert_act(i
->id
, i
->get_title(), !i
->params
.empty(), i
->is_toggle());
281 for(auto i
: item_by_action
)
282 i
.second
->Enable(our_rom
->rtype
->action_flags(i
.first
) & 1);
283 for(auto i
: toggles
)
284 item_by_action
[i
]->Check(our_rom
->rtype
->action_flags(i
) & 2);
287 std::string
munge_name(const std::string
& orig
)
291 if(r
= regex("(.*)\\(([0-9]+)\\)", newname
)) {
294 sequence
= parse_value
<uint64_t>(r
[2]);
295 newname
= (stringfmt() << r
[1] << "(" << sequence
+ 1 << ")").str();
297 newname
= newname
+ "(2)";
300 newname
= newname
+ "(2)";
305 void handle_watch_load(std::map
<std::string
, std::string
>& new_watches
, std::set
<std::string
>& old_watches
)
307 auto proj
= project_get();
309 for(auto i
: new_watches
) {
310 std::string name
= i
.first
;
312 if(!old_watches
.count(name
)) {
313 set_watchexpr_for(name
, i
.second
);
315 } else if(get_watchexpr_for(name
) == i
.second
)
318 name
= munge_name(name
);
322 for(auto i
: new_watches
)
323 set_watchexpr_for(i
.first
, i
.second
);
324 for(auto i
: old_watches
)
325 if(!new_watches
.count(i
))
326 set_watchexpr_for(i
, "");
330 std::string
get_default_screenshot_name()
332 auto p
= project_get();
336 auto files
= enumerate_directory(p
->directory
, ".*-[0-9]+\\.png");
337 std::set
<std::string
> numbers
;
338 for(auto i
: files
) {
341 split
= i
.find_last_of("\\/");
343 split
= i
.find_last_of("/");
345 std::string name
= i
;
346 if(split
< name
.length())
347 name
= name
.substr(split
+ 1);
348 regex_results r
= regex("(.*)-([0-9]+)\\.png", name
);
349 if(r
[1] != p
->prefix
)
351 numbers
.insert(r
[2]);
353 for(uint64_t i
= 1;; i
++) {
354 std::string candidate
= (stringfmt() << i
).str();
355 if(!numbers
.count(candidate
))
356 return p
->prefix
+ "-" + candidate
+ ".png";
361 std::string
project_prefixname(const std::string ext
)
363 auto p
= project_get();
367 return p
->prefix
+ "." + ext
;
371 double pick_volume(wxWindow
* win
, const std::string
& title
, std::string
& last
)
376 value
= pick_text(win
, title
, "Enter volume in absolute units, percentage (%) or dB:",
378 if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)", value
))
379 parsed
= strtod(r
[1].c_str(), NULL
);
380 else if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)%", value
))
381 parsed
= strtod(r
[1].c_str(), NULL
) / 100;
382 else if(r
= regex("([+-]?([0-9]*.[0-9]+|[0-9]+))dB", value
))
383 parsed
= pow(10, strtod(r
[1].c_str(), NULL
) / 20);
385 wxMessageBox(wxT("Invalid volume"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, win
);
392 void recent_rom_selected(const std::string
& file
)
394 platform::queue("unpause-emulator");
395 platform::queue("reload-rom " + file
);
398 void recent_movie_selected(const std::string
& file
)
400 platform::queue("load-smart " + file
);
405 std::string windowname
= "lsnes rr" + lsnes_version
+ " [";
406 auto p
= project_get();
408 windowname
= windowname
+ p
->name
;
410 windowname
= windowname
+ our_rom
->rtype
->get_core_identifier();
411 windowname
= windowname
+ "]";
412 return towxstring(windowname
);
417 struct loaded_rom
* rom
;
418 struct moviefile
* initial
;
419 bool load_has_to_succeed
;
422 void* emulator_main(void* _args
)
424 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
427 messages
<< "Using core: " << our_rom
->rtype
->get_core_identifier() << std::endl
;
428 struct moviefile
* movie
= args
->initial
;
429 bool has_to_succeed
= args
->load_has_to_succeed
;
430 platform::flush_command_queue();
431 main_loop(*our_rom
, *movie
, has_to_succeed
);
432 signal_program_exit();
433 } catch(std::bad_alloc
& e
) {
435 } catch(std::exception
& e
) {
436 messages
<< "FATAL: " << e
.what() << std::endl
;
437 platform::fatal_error();
442 void join_emulator_thread()
444 emulation_thread
->join();
447 keyboard_mouse_calibration mouse_cal
= {0};
448 keyboard_key_mouse
mouse_x(lsnes_kbd
, "mouse_x", "mouse", mouse_cal
);
449 keyboard_key_mouse
mouse_y(lsnes_kbd
, "mouse_y", "mouse", mouse_cal
);
450 keyboard_key_key
mouse_l(lsnes_kbd
, "mouse_left", "mouse");
451 keyboard_key_key
mouse_m(lsnes_kbd
, "mouse_center", "mouse");
452 keyboard_key_key
mouse_r(lsnes_kbd
, "mouse_right", "mouse");
453 keyboard_key_key
mouse_i(lsnes_kbd
, "mouse_inwindow", "mouse");
455 void handle_wx_mouse(wxMouseEvent
& e
)
457 platform::queue(keypress(keyboard_modifier_set(), mouse_x
, e
.GetX() / horizontal_scale_factor
));
458 platform::queue(keypress(keyboard_modifier_set(), mouse_y
, e
.GetY() / vertical_scale_factor
));
460 platform::queue(keypress(keyboard_modifier_set(), mouse_i
, 1));
462 platform::queue(keypress(keyboard_modifier_set(), mouse_i
, 0));
464 platform::queue(keypress(keyboard_modifier_set(), mouse_l
, 1));
466 platform::queue(keypress(keyboard_modifier_set(), mouse_l
, 0));
468 platform::queue(keypress(keyboard_modifier_set(), mouse_m
, 1));
470 platform::queue(keypress(keyboard_modifier_set(), mouse_m
, 0));
472 platform::queue(keypress(keyboard_modifier_set(), mouse_r
, 1));
474 platform::queue(keypress(keyboard_modifier_set(), mouse_r
, 0));
477 bool is_readonly_mode()
480 runemufn([&ret
]() { ret
= movb
.get_movie().readonly_mode(); });
484 std::pair
<int, int> UI_controller_index_by_logical(unsigned lid
)
486 std::pair
<int, int> ret
;
487 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
491 void set_speed(double target
)
494 set_speed_multiplier(std::numeric_limits
<double>::infinity());
496 set_speed_multiplier(target
/ 100);
499 class broadcast_listener
: public information_dispatch
502 broadcast_listener(wxwin_mainwindow
* win
);
503 void on_sound_unmute(bool unmute
) throw();
504 void on_mode_change(bool readonly
) throw();
505 void on_core_change();
508 wxwin_mainwindow
* mainw
;
511 broadcast_listener::broadcast_listener(wxwin_mainwindow
* win
)
512 : information_dispatch("wxwidgets-broadcast-listener")
517 void broadcast_listener::on_core_change()
519 signal_core_change();
522 void broadcast_listener::on_sound_unmute(bool unmute
) throw()
524 runuifun([this, unmute
]() { this->mainw
->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
527 void broadcast_listener::on_mode_change(bool readonly
) throw()
529 runuifun([this, readonly
]() { this->mainw
->menu_check(wxID_READONLY_MODE
, readonly
); });
532 void update_preferences()
534 preferred_core
.clear();
535 for(auto i
: core_type::get_core_types()) {
536 std::string val
= i
->get_hname() + " / " + i
->get_core_identifier();
537 for(auto j
: i
->get_extensions()) {
538 std::string key
= "ext:" + j
;
539 if(core_selections
.count(key
) && core_selections
[key
] == val
)
540 preferred_core
[key
] = i
;
542 std::string key2
= "type:" + i
->get_iname();
543 if(core_selections
.count(key2
) && core_selections
[key2
] == val
)
544 preferred_core
[key2
] = i
;
549 void broadcast_listener::on_new_core()
551 update_preferences();
554 std::string
movie_path()
556 return lsnes_vset
["moviepath"].str();
559 std::string
rom_path()
561 return lsnes_vset
["rompath"].str();
564 bool is_lsnes_movie(const std::string
& filename
)
567 zip_reader
r(filename
);
568 std::istream
& s
= r
["systemid"];
573 return (s2
== "lsnes-rr1");
579 class loadfile
: public wxFileDropTarget
582 loadfile(wxwin_mainwindow
* win
) : pwin(win
) {};
583 bool OnDropFiles(wxCoord x
, wxCoord y
, const wxArrayString
& filenames
)
586 if(filenames
.Count() == 2) {
587 if(is_lsnes_movie(tostdstring(filenames
[0])) &&
588 !is_lsnes_movie(tostdstring(filenames
[1]))) {
589 platform::queue("unpause-emulator");
590 platform::queue("reload-rom " + tostdstring(filenames
[1]));
591 platform::queue("load-smart " + tostdstring(filenames
[0]));
594 if(!is_lsnes_movie(tostdstring(filenames
[0])) &&
595 is_lsnes_movie(tostdstring(filenames
[1]))) {
596 platform::queue("unpause-emulator");
597 platform::queue("reload-rom " + tostdstring(filenames
[0]));
598 platform::queue("load-smart " + tostdstring(filenames
[1]));
602 if(filenames
.Count() == 1) {
603 if(is_lsnes_movie(tostdstring(filenames
[0]))) {
604 platform::queue("load-smart " + tostdstring(filenames
[0]));
605 pwin
->recent_movies
->add(tostdstring(filenames
[0]));
608 platform::queue("unpause-emulator");
609 platform::queue("reload-rom " + tostdstring(filenames
[0]));
610 pwin
->recent_roms
->add(tostdstring(filenames
[0]));
616 wxwin_mainwindow
* pwin
;
620 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
)
622 update_preferences();
624 struct emu_args
* a
= new emu_args
;
627 a
->load_has_to_succeed
= false;
628 modal_pause_holder hld
;
629 emulation_thread
= new thread_class(emulator_main
, a
);
630 main_window
= new wxwin_mainwindow();
632 } catch(std::bad_alloc
& e
) {
637 wxwin_mainwindow::panel::panel(wxWindow
* win
)
638 : wxPanel(win
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
)
640 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
641 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
642 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
643 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
644 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
645 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
646 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
647 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
648 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
649 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
650 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
651 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
652 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
653 SetMinSize(wxSize(512, 448));
656 void wxwin_mainwindow::menu_start(wxString name
)
658 while(!upper
.empty())
660 current_menu
= new wxMenu();
661 menubar
->Append(current_menu
, name
);
664 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
666 while(!upper
.empty())
668 menubar
->Append(menu
, name
);
672 void wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
674 current_menu
->AppendSubMenu(menu
, name
);
677 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
679 current_menu
->Append(id
, name
);
680 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
681 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
684 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
686 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
687 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
688 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
691 void wxwin_mainwindow::menu_start_sub(wxString name
)
693 wxMenu
* old
= current_menu
;
694 upper
.push(current_menu
);
695 current_menu
= new wxMenu();
696 old
->AppendSubMenu(current_menu
, name
);
699 void wxwin_mainwindow::menu_end_sub()
701 current_menu
= upper
.top();
705 bool wxwin_mainwindow::menu_ischecked(int id
)
707 if(checkitems
.count(id
))
708 return checkitems
[id
]->IsChecked();
713 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
715 if(checkitems
.count(id
))
716 return checkitems
[id
]->Check(newstate
);
721 void wxwin_mainwindow::menu_enable(int id
, bool newstate
)
723 auto item
= menubar
->FindItem(id
);
726 item
->Enable(newstate
);
729 void wxwin_mainwindow::menu_separator()
731 current_menu
->AppendSeparator();
734 void wxwin_mainwindow::panel::request_paint()
739 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
741 render_framebuffer();
742 static struct SwsContext
* ctx
;
749 bool aux
= hflip_enabled
|| vflip_enabled
|| rotate_enabled
;
751 tw
= main_screen
.get_height() * horizontal_scale_factor
+ 0.5;
752 th
= main_screen
.get_width() * vertical_scale_factor
+ 0.5;
754 tw
= main_screen
.get_width() * horizontal_scale_factor
+ 0.5;
755 th
= main_screen
.get_height() * vertical_scale_factor
+ 0.5;
758 main_window_dirty
= false;
761 if(!screen_buffer
|| tw
!= old_width
|| th
!= old_height
|| scaling_flags
!= old_flags
||
762 hflip_enabled
!= old_hflip
|| vflip_enabled
!= old_vflip
|| rotate_enabled
!= old_rotate
) {
764 delete[] screen_buffer
;
766 delete[] rotate_buffer
;
769 old_flags
= scaling_flags
;
770 old_hflip
= hflip_enabled
;
771 old_vflip
= vflip_enabled
;
772 old_rotate
= rotate_enabled
;
773 uint32_t w
= main_screen
.get_width();
774 uint32_t h
= main_screen
.get_height();
776 ctx
= sws_getCachedContext(ctx
, rotate_enabled
? h
: w
, rotate_enabled
? w
: h
, PIX_FMT_RGBA
,
777 tw
, th
, PIX_FMT_BGR24
, scaling_flags
, NULL
, NULL
, NULL
);
778 tw
= max(tw
, static_cast<uint32_t>(128));
779 th
= max(th
, static_cast<uint32_t>(112));
780 screen_buffer
= new unsigned char[tw
* th
* 3];
782 rotate_buffer
= new uint32_t[main_screen
.get_width() * main_screen
.get_height()];
783 SetMinSize(wxSize(tw
, th
));
784 signal_resize_needed();
787 //Hflip, Vflip or rotate active.
788 size_t width
= main_screen
.get_width();
789 size_t height
= main_screen
.get_height();
790 size_t width1
= width
- 1;
791 size_t height1
= height
- 1;
792 size_t stride
= main_screen
.rowptr(1) - main_screen
.rowptr(0);
793 uint32_t* pixels
= main_screen
.rowptr(0);
795 for(unsigned y
= 0; y
< height
; y
++) {
796 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
797 uint32_t* dpixels
= rotate_buffer
+ (height1
- y
);
799 for(unsigned x
= 0; x
< width
; x
++)
800 dpixels
[x
* height
] = pixels2
[width1
- x
];
802 for(unsigned x
= 0; x
< width
; x
++)
803 dpixels
[x
* height
] = pixels2
[x
];
806 for(unsigned y
= 0; y
< height
; y
++) {
807 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
808 uint32_t* dpixels
= rotate_buffer
+ y
* width
;
810 for(unsigned x
= 0; x
< width
; x
++)
811 dpixels
[x
] = pixels2
[width1
- x
];
813 for(unsigned x
= 0; x
< width
; x
++)
814 dpixels
[x
] = pixels2
[x
];
818 srcs
[0] = 4 * (rotate_enabled
? main_screen
.get_height() : main_screen
.get_width());
820 srcp
[0] = reinterpret_cast<unsigned char*>(aux
? rotate_buffer
: main_screen
.rowptr(0));
821 dstp
[0] = screen_buffer
;
822 memset(screen_buffer
, 0, tw
* th
* 3);
823 if(main_screen
.get_width() && main_screen
.get_height())
824 sws_scale(ctx
, srcp
, srcs
, 0, rotate_enabled
? main_screen
.get_width() : main_screen
.get_height(),
826 wxBitmap
bmp(wxImage(tw
, th
, screen_buffer
, true));
827 dc
.DrawBitmap(bmp
, 0, 0, false);
828 main_window_dirty
= false;
831 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
836 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
838 handle_wx_keyboard(e
, true);
841 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
843 handle_wx_keyboard(e
, false);
846 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
851 wxwin_mainwindow::wxwin_mainwindow()
852 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
853 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
855 broadcast_listener
* blistener
= new broadcast_listener(this);
858 toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
859 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
860 toplevel
->Add(spanel
= new wxwin_status::panel(this, gpanel
, 20), 1, wxGROW
);
862 toplevel
->SetSizeHints(this);
866 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
867 SetMenuBar(menubar
= new wxMenuBar
);
868 SetStatusBar(statusbar
= new wxStatusBar(this));
870 menu_start(wxT("File"));
871 menu_start_sub(wxT("New"));
872 menu_entry(wxID_NEW_MOVIE
, wxT("Movie..."));
873 menu_entry(wxID_NEW_PROJECT
, wxT("Project..."));
875 menu_start_sub(wxT("Load"));
876 menu_entry(wxID_LOAD_STATE
, wxT("State..."));
877 menu_entry(wxID_LOAD_STATE_RO
, wxT("State (readonly)..."));
878 menu_entry(wxID_LOAD_STATE_RW
, wxT("State (read-write)..."));
879 menu_entry(wxID_LOAD_STATE_P
, wxT("State (preserve input)..."));
880 menu_entry(wxID_LOAD_MOVIE
, wxT("Movie..."));
881 if(loaded_library::call_library() != "") {
883 menu_entry(wxID_LOAD_LIBRARY
, towxstring(std::string("Load ") + loaded_library::call_library()));
886 menu_entry(wxID_RELOAD_ROM_IMAGE
, wxT("Reload ROM"));
887 menu_entry(wxID_LOAD_ROM_IMAGE
, wxT("ROM..."));
888 menu_entry(wxID_LOAD_PROJECT
, wxT("Project..."));
890 menu_special_sub(wxT("Recent ROMs"), recent_roms
= new recent_menu(this, wxID_RROM_FIRST
, wxID_RROM_LAST
,
891 get_config_path() + "/recent-roms.txt", recent_rom_selected
));
892 menu_special_sub(wxT("Recent Movies"), recent_movies
= new recent_menu(this, wxID_RMOVIE_FIRST
,
893 wxID_RMOVIE_LAST
, get_config_path() + "/recent-movies.txt", recent_movie_selected
));
895 menu_entry(wxID_CONFLICTRESOLUTION
, wxT("Conflict resolution"));
897 menu_start_sub(wxT("Save"));
898 menu_entry(wxID_SAVE_STATE
, wxT("State..."));
899 menu_entry(wxID_SAVE_MOVIE
, wxT("Movie..."));
900 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Screenshot..."));
901 menu_entry(wxID_SAVE_SUBTITLES
, wxT("Subtitles..."));
902 menu_entry(wxID_CANCEL_SAVES
, wxT("Cancel pending saves"));
904 menu_start_sub(wxT("Close"));
905 menu_entry(wxID_CLOSE_PROJECT
, wxT("Project"));
906 menu_entry(wxID_CLOSE_ROM
, wxT("ROM"));
907 menu_enable(wxID_CLOSE_PROJECT
, project_get() != NULL
);
908 menu_enable(wxID_CLOSE_ROM
, project_get() == NULL
);
911 menu_entry(wxID_EXIT
, wxT("Quit"));
913 menu_special(wxT("System"), reinterpret_cast<wxMenu
*>(sysmenu
= new system_menu(this)));
915 menu_start(wxT("Movie"));
916 menu_entry_check(wxID_READONLY_MODE
, wxT("Readonly mode"));
917 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
918 menu_entry(wxID_EDIT_AUTHORS
, wxT("Edit game name && authors..."));
919 menu_entry(wxID_EDIT_SUBTITLES
, wxT("Edit subtitles..."));
920 menu_entry(wxID_EDIT_VSUBTITLES
, wxT("Edit commantary track..."));
922 menu_entry(wxID_REWIND_MOVIE
, wxT("Rewind to start"));
924 menu_start(wxT("Speed"));
925 menu_entry(wxID_SPEED_5
, wxT("1/20x"));
926 menu_entry(wxID_SPEED_10
, wxT("1/10x"));
927 menu_entry(wxID_SPEED_17
, wxT("1/6x"));
928 menu_entry(wxID_SPEED_20
, wxT("1/5x"));
929 menu_entry(wxID_SPEED_25
, wxT("1/4x"));
930 menu_entry(wxID_SPEED_33
, wxT("1/3x"));
931 menu_entry(wxID_SPEED_50
, wxT("1/2x"));
932 menu_entry(wxID_SPEED_100
, wxT("1x"));
933 menu_entry(wxID_SPEED_150
, wxT("1.5x"));
934 menu_entry(wxID_SPEED_200
, wxT("2x"));
935 menu_entry(wxID_SPEED_300
, wxT("3x"));
936 menu_entry(wxID_SPEED_500
, wxT("5x"));
937 menu_entry(wxID_SPEED_1000
, wxT("10x"));
938 menu_entry(wxID_SPEED_TURBO
, wxT("Turbo"));
939 menu_entry(wxID_SET_SPEED
, wxT("Set..."));
941 menu_start(wxT("Tools"));
942 menu_entry(wxID_RUN_SCRIPT
, wxT("Run batch file..."));
944 menu_entry(wxID_EVAL_LUA
, wxT("Evaluate Lua statement..."));
945 menu_entry(wxID_RUN_LUA
, wxT("Run Lua script..."));
947 menu_entry(wxID_RESET_LUA
, wxT("Reset Lua VM"));
949 menu_entry(wxID_AUTOHOLD
, wxT("Autohold/Autofire..."));
950 menu_entry(wxID_TASINPUT
, wxT("TAS input plugin..."));
951 menu_entry(wxID_EDIT_MACROS
, wxT("Edit macros..."));
953 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch..."));
955 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch..."));
956 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch..."));
958 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search..."));
960 menu_entry(wxID_MOVIE_EDIT
, wxT("Edit movie..."));
962 menu_special_sub(wxT("Video Capture"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
963 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
965 menu_start(wxT("Configure"));
966 menu_entry_check(wxID_SHOW_STATUS
, wxT("Show status panel"));
967 menu_check(wxID_SHOW_STATUS
, true);
968 menu_entry_check(wxID_DEDICATED_MEMORY_WATCH
, wxT("Dedicated memory watch"));
969 menu_entry(wxID_SHOW_MESSAGES
, wxT("Show messages"));
970 menu_entry(wxID_SETTINGS
, wxT("Configure emulator..."));
971 menu_entry(wxID_SETTINGS_HOTKEYS
, wxT("Configure hotkeys..."));
972 menu_entry(wxID_SETTINGS_CONTROLLERS
, wxT("Configure controllers..."));
973 if(audioapi_driver_initialized()) {
975 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("Sounds enabled"));
976 menu_entry(wxID_VUDISPLAY
, wxT("VU display / volume controls"));
977 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
978 menu_entry(wxID_AUDIO_DEVICE
, wxT("Set audio device"));
981 menu_start(wxT("Help"));
982 menu_entry(wxID_ABOUT
, wxT("About..."));
984 gpanel
->SetDropTarget(new loadfile(this));
985 spanel
->SetDropTarget(new loadfile(this));
988 void wxwin_mainwindow::request_paint()
993 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
995 //Veto it for now, latter things will delete it.
997 platform::queue("quit-emulator");
1000 void wxwin_mainwindow::notify_update() throw()
1002 if(!main_window_dirty
) {
1003 main_window_dirty
= true;
1008 void wxwin_mainwindow::notify_resized() throw()
1011 toplevel
->SetSizeHints(this);
1015 void wxwin_mainwindow::notify_update_status() throw()
1017 spanel
->request_paint();
1019 mwindow
->notify_update();
1022 void wxwin_mainwindow::notify_exit() throw()
1024 wxwidgets_exiting
= true;
1025 join_emulator_thread();
1029 std::u32string
read_variable_map(const std::map
<std::string
, std::u32string
>& vars
, const std::string
& key
)
1031 if(!vars
.count(key
))
1033 return vars
.find(key
)->second
;
1036 void wxwin_mainwindow::update_statusbar(const std::map
<std::string
, std::u32string
>& vars
)
1041 std::basic_ostringstream
<char32_t
> s
;
1042 bool recording
= (read_variable_map(vars
, "!mode") == U
"R");
1044 s
<< U
"Frame: " << read_variable_map(vars
, "!frame");
1046 s
<< U
"Frame: " << read_variable_map(vars
, "!frame") << U
"/" <<
1047 read_variable_map(vars
, "!length");
1048 s
<< U
" Lag: " << read_variable_map(vars
, "!lag");
1049 s
<< U
" Subframe: " << read_variable_map(vars
, "!subframe");
1050 if(vars
.count("!saveslot"))
1051 s
<< U
" Slot: " << read_variable_map(vars
, "!saveslot");
1052 if(vars
.count("!saveslotinfo"))
1053 s
<< U
" [" << read_variable_map(vars
, "!saveslotinfo") << U
"]";
1054 s
<< U
" Speed: " << read_variable_map(vars
, "!speed") << U
"%";
1056 if(read_variable_map(vars
, "!dumping") != U
"")
1058 if(read_variable_map(vars
, "!mode") == U
"C")
1060 else if(read_variable_map(vars
, "!mode") == U
"R")
1062 else if(read_variable_map(vars
, "!mode") == U
"P")
1064 else if(read_variable_map(vars
, "!mode") == U
"F")
1068 std::u32string macros
= read_variable_map(vars
, "!macros");
1070 s
<< U
" Macros: " << macros
;
1072 statusbar
->SetStatusText(towxstring(s
.str()));
1073 } catch(std::exception
& e
) {
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::handle_menu_click(wxCommandEvent
& e
)
1084 handle_menu_click_cancelable(e
);
1085 } catch(canceled_exception
& e
) {
1087 } catch(std::bad_alloc
& e
) {
1089 } catch(std::exception
& e
) {
1090 show_message_ok(this, "Error in menu handler", e
.what(), wxICON_EXCLAMATION
);
1094 void wxwin_mainwindow::refresh_title() throw()
1096 SetTitle(getname());
1097 auto p
= project_get();
1098 menu_enable(wxID_RELOAD_ROM_IMAGE
, !p
);
1099 menu_enable(wxID_LOAD_ROM_IMAGE
, !p
);
1100 menu_enable(wxID_CLOSE_PROJECT
, p
!= NULL
);
1101 menu_enable(wxID_CLOSE_ROM
, p
== NULL
);
1102 reinterpret_cast<system_menu
*>(sysmenu
)->update(false);
1103 menubar
->SetMenuLabel(1, towxstring(our_rom
->rtype
->get_systemmenu_name()));
1106 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent
& e
)
1108 std::string filename
;
1111 case wxID_FRAMEADVANCE
:
1112 platform::queue("+advance-frame");
1113 platform::queue("-advance-frame");
1115 case wxID_SUBFRAMEADVANCE
:
1116 platform::queue("+advance-poll");
1117 platform::queue("-advance-poll");
1120 platform::queue("advance-skiplag");
1123 platform::queue("pause-emulator");
1126 platform::queue("quit-emulator");
1128 case wxID_AUDIO_ENABLED
:
1129 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
1131 case wxID_AUDIO_DEVICE
:
1132 wxeditor_sounddev_display(this);
1134 case wxID_CANCEL_SAVES
:
1135 platform::queue("cancel-saves");
1137 case wxID_LOAD_MOVIE
:
1138 filename
= pick_file(this, "Load Movie", project_moviepath(), false, "lsmv");
1139 recent_movies
->add(filename
);
1140 platform::queue("load-movie " + filename
);
1142 case wxID_LOAD_STATE
:
1143 filename
= pick_file(this, "Load State", project_moviepath(), false, project_savestate_ext());
1144 recent_movies
->add(filename
);
1145 platform::queue("load " + filename
);
1147 case wxID_LOAD_STATE_RO
:
1148 filename
= pick_file(this, "Load State (Read-Only)", project_moviepath(), false,
1149 project_savestate_ext());
1150 recent_movies
->add(filename
);
1151 platform::queue("load-readonly " + filename
);
1153 case wxID_LOAD_STATE_RW
:
1154 filename
= pick_file(this, "Load State (Read-Write)", project_moviepath(), false,
1155 project_savestate_ext());
1156 recent_movies
->add(filename
);
1157 platform::queue("load-state " + filename
);
1159 case wxID_LOAD_STATE_P
:
1160 filename
= pick_file(this, "Load State (Preserve)", project_moviepath(), false,
1161 project_savestate_ext());
1162 recent_movies
->add(filename
);
1163 platform::queue("load-preserve " + filename
);
1165 case wxID_REWIND_MOVIE
:
1166 platform::queue("rewind-movie");
1168 case wxID_SAVE_MOVIE
:
1169 filename
= pick_file(this, "Save Movie", project_moviepath(), true, "lsmv",
1170 project_prefixname("lsmv"));
1171 recent_movies
->add(filename
);
1172 platform::queue("save-movie " + filename
);
1174 case wxID_SAVE_SUBTITLES
:
1175 platform::queue("save-subtitle " + pick_file(this, "Save Subtitle (.sub)", project_moviepath(), true,
1176 "sub", project_prefixname("sub")));
1178 case wxID_SAVE_STATE
:
1179 filename
= pick_file(this, "Save State", project_moviepath(), true, project_savestate_ext());
1180 recent_movies
->add(filename
);
1181 platform::queue("save-state " + filename
);
1183 case wxID_SAVE_SCREENSHOT
:
1184 platform::queue("take-screenshot " + pick_file(this, "Save Screenshot", project_moviepath(), true,
1185 "png", get_default_screenshot_name()));
1187 case wxID_RUN_SCRIPT
:
1188 platform::queue("run-script " + pick_file_member(this, "Select Script", project_otherpath()));
1191 platform::queue("run-lua " + pick_file(this, "Select Lua Script", project_otherpath(), false, "lua"));
1193 case wxID_RESET_LUA
:
1194 platform::queue("reset-lua");
1197 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
1199 case wxID_READONLY_MODE
:
1200 s
= menu_ischecked(wxID_READONLY_MODE
);
1202 movb
.get_movie().readonly_mode(s
);
1204 lua_callback_do_readwrite();
1205 update_movie_state();
1206 graphics_driver_notify_status();
1210 wxeditor_autohold_display(this);
1212 case wxID_EDIT_AUTHORS
:
1213 wxeditor_authors_display(this);
1215 case wxID_EDIT_MACROS
:
1216 wxeditor_macro_display(this);
1218 case wxID_EDIT_SUBTITLES
:
1219 wxeditor_subtitles_display(this);
1221 case wxID_EDIT_VSUBTITLES
:
1222 show_wxeditor_voicesub(this);
1224 case wxID_EDIT_MEMORYWATCH
:
1225 wxeditor_memorywatch_display(this);
1227 case wxID_SAVE_MEMORYWATCH
: {
1228 modal_pause_holder hld
;
1229 std::set
<std::string
> old_watches
;
1230 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1231 std::string filename
= pick_file(this, "Save watches to file", project_otherpath(), true, "lwch");
1232 std::ofstream
out(filename
.c_str());
1233 for(auto i
: old_watches
) {
1235 runemufn([i
, &val
]() { val
= get_watchexpr_for(i
); });
1236 out
<< i
<< std::endl
<< val
<< std::endl
;
1241 case wxID_LOAD_MEMORYWATCH
: {
1242 modal_pause_holder hld
;
1243 std::set
<std::string
> old_watches
;
1244 runemufn([&old_watches
]() { old_watches
= get_watches(); });
1245 std::map
<std::string
, std::string
> new_watches
;
1246 std::string filename
= pick_file(this, "Choose memory watch file", project_otherpath(), "lwch");
1248 std::istream
& in
= open_file_relative(filename
, "");
1252 std::getline(in
, wname
);
1253 std::getline(in
, wexpr
);
1254 new_watches
[strip_CR(wname
)] = strip_CR(wexpr
);
1257 } catch(std::exception
& e
) {
1258 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e
.what(),
1259 wxICON_EXCLAMATION
);
1263 runemufn([&new_watches
, &old_watches
]() {
1264 handle_watch_load(new_watches
, old_watches
);
1268 case wxID_MEMORY_SEARCH
:
1269 wxwindow_memorysearch_display();
1272 wxeditor_tasinput_display(this);
1275 std::ostringstream str
;
1276 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1277 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1278 for(auto i
: core_core::all_cores())
1280 str
<< "Core: " << i
->get_core_identifier() << std::endl
;
1281 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1284 case wxID_SHOW_STATUS
: {
1285 bool newstate
= menu_ischecked(wxID_SHOW_STATUS
);
1288 if(newstate
&& !spanel_shown
)
1289 toplevel
->Add(spanel
, 1, wxGROW
);
1290 else if(!newstate
&& spanel_shown
)
1291 toplevel
->Detach(spanel
);
1294 spanel_shown
= newstate
;
1296 toplevel
->SetSizeHints(this);
1300 case wxID_DEDICATED_MEMORY_WATCH
: {
1301 bool newstate
= menu_ischecked(wxID_DEDICATED_MEMORY_WATCH
);
1302 if(newstate
&& !mwindow
) {
1303 mwindow
= new wxwin_status(-1, "Memory Watch");
1304 spanel
->set_watch_flag(1);
1306 } else if(!newstate
&& mwindow
) {
1309 spanel
->set_watch_flag(0);
1313 case wxID_SET_SPEED
: {
1314 std::string value
= "infinite";
1315 double val
= get_speed_multiplier();
1316 if(!(val
== std::numeric_limits
<double>::infinity()))
1317 value
= (stringfmt() << (100 * val
)).str();
1318 value
= pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value
);
1320 if(value
== "infinite")
1321 set_speed_multiplier(std::numeric_limits
<double>::infinity());
1323 double v
= parse_value
<double>(value
) / 100;
1326 set_speed_multiplier(v
);
1329 wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, this);
1340 set_speed(16.66666666666);
1349 set_speed(33.3333333333333);
1354 case wxID_SPEED_100
:
1357 case wxID_SPEED_150
:
1360 case wxID_SPEED_200
:
1363 case wxID_SPEED_300
:
1366 case wxID_SPEED_500
:
1369 case wxID_SPEED_1000
:
1372 case wxID_SPEED_TURBO
:
1375 case wxID_LOAD_LIBRARY
: {
1376 std::string name
= std::string("load ") + loaded_library::call_library();
1377 with_loaded_library(new loaded_library(pick_file(this, name
, project_otherpath(), false,
1378 loaded_library::call_library_ext())));
1379 handle_post_loadlibrary();
1383 wxsetingsdialog_display(this, 0);
1385 case wxID_SETTINGS_HOTKEYS
:
1386 wxsetingsdialog_display(this, 1);
1388 case wxID_SETTINGS_CONTROLLERS
:
1389 wxsetingsdialog_display(this, 2);
1391 case wxID_LOAD_ROM_IMAGE
:
1392 do_load_rom_image();
1394 case wxID_RELOAD_ROM_IMAGE
:
1395 platform::queue("unpause-emulator");
1396 platform::queue("reload-rom");
1398 case wxID_NEW_MOVIE
:
1399 show_projectwindow(this);
1401 case wxID_SHOW_MESSAGES
:
1402 msg_window
->reshow();
1404 case wxID_CONFLICTRESOLUTION
:
1405 show_conflictwindow(this);
1407 case wxID_VUDISPLAY
:
1408 open_vumeter_window(this);
1410 case wxID_MOVIE_EDIT
:
1411 wxeditor_movie_display(this);
1413 case wxID_NEW_PROJECT
:
1414 open_new_project_window(this);
1416 case wxID_LOAD_PROJECT
: {
1417 auto projects
= project_enumerate();
1418 std::vector
<std::string
> a
;
1419 std::vector
<wxString
> b
;
1420 for(auto i
: projects
) {
1421 a
.push_back(i
.first
);
1422 b
.push_back(towxstring(i
.second
));
1425 show_message_ok(this, "Load project", "No projects available", wxICON_EXCLAMATION
);
1428 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(this, wxT("Select project to switch to:"),
1429 wxT("Load project"), b
.size(), &b
[0]);
1430 if(d2
->ShowModal() == wxID_CANCEL
) {
1432 throw canceled_exception();
1434 std::string id
= a
[d2
->GetSelection()];
1435 runemufn([id
]() -> void {
1437 delete &project_load(id
); //Check.
1438 switch_projects(id
);
1439 } catch(std::exception
& e
) {
1440 messages
<< "Failed to change project: " << e
.what() << std::endl
;
1446 case wxID_CLOSE_PROJECT
:
1447 runemufn([]() -> void { project_set(NULL
); });
1449 case wxID_CLOSE_ROM
:
1450 runemufn([]() -> void { close_rom(); });
1455 void wxwin_mainwindow::do_load_rom_image()
1457 wxwindow_romload
r(rom_path());
1460 std::string file
= r
.get_filename();
1461 std::string core
= r
.get_core();
1462 std::string type
= r
.get_type();
1463 std::string filename
;
1465 filename
= file
+ " <" + mangle_name(core
) + "/" + mangle_name(type
) + ">";
1468 recent_roms
->add(filename
);
1469 platform::queue("unpause-emulator");
1470 platform::queue("reload-rom " + filename
);
1473 void wxwin_mainwindow::action_updated()
1475 reinterpret_cast<system_menu
*>(sysmenu
)->update(true);