4 #include "platform/wxwidgets/menu_dump.hpp"
5 #include "platform/wxwidgets/menu_upload.hpp"
6 #include "platform/wxwidgets/platform.hpp"
7 #include "platform/wxwidgets/loadsave.hpp"
8 #include "platform/wxwidgets/window_mainwindow.hpp"
9 #include "platform/wxwidgets/window_messages.hpp"
10 #include "platform/wxwidgets/window_status.hpp"
11 #include "platform/wxwidgets/window-romload.hpp"
12 #include "platform/wxwidgets/settings-common.hpp"
13 #include "platform/wxwidgets/menu_tracelog.hpp"
14 #include "platform/wxwidgets/menu_branches.hpp"
15 #include "platform/wxwidgets/menu_projects.hpp"
17 #include "core/audioapi.hpp"
18 #include "core/command.hpp"
19 #include "core/controller.hpp"
20 #include "core/controllerframe.hpp"
21 #include "core/dispatch.hpp"
22 #include "core/framebuffer.hpp"
23 #include "core/framerate.hpp"
24 #include "core/keymapper.hpp"
25 #include "interface/romtype.hpp"
26 #include "core/loadlib.hpp"
27 #include "lua/lua.hpp"
28 #include "core/mainloop.hpp"
29 #include "core/memorywatch.hpp"
30 #include "core/misc.hpp"
31 #include "core/moviedata.hpp"
32 #include "core/project.hpp"
33 #include "core/romloader.hpp"
34 #include "core/settings.hpp"
35 #include "core/window.hpp"
36 #include "library/directory.hpp"
37 #include "library/minmax.hpp"
38 #include "library/string.hpp"
39 #include "library/zip.hpp"
40 #if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
52 #define UINT64_C(val) val##ULL
54 #include <libswscale/swscale.h>
59 wxID_PAUSE
= wxID_HIGHEST
+ 1,
77 wxID_EDIT_MEMORYWATCH
,
78 wxID_SAVE_MEMORYWATCH
,
79 wxID_LOAD_MEMORYWATCH
,
83 wxID_DUMP_LAST
= wxID_DUMP_FIRST
+ 1023,
104 wxID_RELOAD_ROM_IMAGE
,
105 wxID_LOAD_ROM_IMAGE_FIRST
,
106 wxID_LOAD_ROM_IMAGE_LAST
= wxID_LOAD_ROM_IMAGE_FIRST
+ 1023,
109 wxID_DEDICATED_MEMORY_WATCH
,
111 wxID_RMOVIE_LAST
= wxID_RMOVIE_FIRST
+ 16,
113 wxID_RROM_LAST
= wxID_RROM_FIRST
+ 16,
114 wxID_CONFLICTRESOLUTION
,
122 wxID_ENTER_FULLSCREEN
,
124 wxID_ACTIONS_LAST
= wxID_ACTIONS_FIRST
+ 256,
126 wxID_SETTINGS_LAST
= wxID_SETTINGS_FIRST
+ 256,
131 wxID_RLUA_LAST
= wxID_RLUA_FIRST
+ 16,
133 wxID_UPLOAD_LAST
= wxID_UPLOAD_FIRST
+ 256,
136 wxID_TRACELOG_LAST
= wxID_TRACELOG_FIRST
+ 256,
139 wxID_BRANCH_LAST
= wxID_BRANCH_FIRST
+ 10240,
141 wxID_PROJECT_LAST
= wxID_PROJECT_FIRST
+ 17,
146 double video_scale_factor
= 1.0;
147 int scaling_flags
= SWS_POINT
;
148 bool arcorrect_enabled
= false;
149 bool hflip_enabled
= false;
150 bool vflip_enabled
= false;
151 bool rotate_enabled
= false;
155 std::string last_volume
= "0dB";
156 std::string last_volume_record
= "0dB";
157 std::string last_volume_voice
= "0dB";
158 unsigned char* screen_buffer
;
159 struct SwsContext
* sws_ctx
;
160 uint32_t* rotate_buffer
;
163 int old_flags
= SWS_POINT
;
164 bool old_hflip
= false;
165 bool old_vflip
= false;
166 bool old_rotate
= false;
167 bool main_window_dirty
;
169 bool hashing_in_progress
= false;
170 uint64_t hashing_left
= 0;
171 uint64_t hashing_total
= 0;
172 int64_t last_update
= 0;
173 thread_class
* emulation_thread
;
175 settingvar::variable
<settingvar::model_bool
<settingvar::yes_no
>> background_audio(lsnes_vset
,
176 "background-audio", "GUIā£Enable background audio", true);
178 class _focus_timer
: public wxTimer
183 was_focused
= (wxWindow::FindFocus() != NULL
);
184 was_enabled
= platform::is_sound_enabled();
189 bool is_focused
= (wxWindow::FindFocus() != NULL
);
190 if(is_focused
&& !was_focused
) {
192 if(!background_audio
)
193 platform::sound_enable(was_enabled
);
194 } else if(!is_focused
&& was_focused
) {
196 was_enabled
= platform::is_sound_enabled();
197 if(!background_audio
)
198 platform::sound_enable(false);
200 was_focused
= is_focused
;
207 class download_timer
: public wxTimer
210 download_timer(wxwin_mainwindow
* main
)
217 if(w
->download_in_progress
->finished
) {
218 w
->update_statusbar(std::map
<std::string
, std::u32string
>());
219 auto old
= w
->download_in_progress
;
220 w
->download_in_progress
= NULL
;
221 if(old
->errormsg
!= "") {
222 show_message_ok(w
, "Error downloading movie", old
->errormsg
,
225 platform::queue("load-movie $MEMORY:wxwidgets_download_tmp");
231 w
->update_statusbar(std::map
<std::string
, std::u32string
>());
238 void hash_callback(uint64_t left
, uint64_t total
)
240 wxwin_mainwindow
* mwin
= main_window
;
241 if(left
== 0xFFFFFFFFFFFFFFFFULL
) {
242 hashing_in_progress
= false;
243 runuifun([mwin
]() { if(mwin
) mwin
->notify_update_status(); });
244 last_update
= get_utime() - 2000000;
247 hashing_in_progress
= true;
249 hashing_total
= total
;
250 uint64_t this_update
= get_utime();
251 if(this_update
< last_update
- 1000000 || this_update
> last_update
+ 1000000) {
252 runuifun([mwin
]() { if(mwin
) mwin
->notify_update_status(); });
253 last_update
= this_update
;
257 std::pair
<std::string
, std::string
> lsplit(std::string l
)
259 for(unsigned i
= 0; i
< l
.length() - 3; i
++)
260 if((uint8_t)l
[i
] == 0xE2 && (uint8_t)l
[i
+ 1] == 0x80 && (uint8_t)l
[i
+ 2] == 0xA3)
261 return std::make_pair(l
.substr(0, i
), l
.substr(i
+ 3));
262 return std::make_pair("", l
);
265 recentfiles::multirom
loadreq_to_multirom(const romload_request
& req
)
267 recentfiles::multirom r
;
268 r
.packfile
= req
.packfile
;
269 r
.singlefile
= req
.singlefile
;
271 r
.system
= req
.system
;
272 r
.region
= req
.region
;
273 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++)
274 if(req
.files
[i
] != "") {
275 r
.files
.resize(i
+ 1);
276 r
.files
[i
] = req
.files
[i
];
281 class system_menu
: public wxMenu
284 system_menu(wxWindow
* win
);
286 void on_select(wxCommandEvent
& e
);
287 void update(bool light
);
290 void insert_pass(int id
, const std::string
& label
);
291 void insert_act(unsigned id
, const std::string
& label
, bool dots
, bool check
);
292 wxMenu
* lookup_menu(const std::string
& key
);
294 std::map
<int, unsigned> action_by_id
;
295 std::map
<unsigned, wxMenuItem
*> item_by_action
;
296 std::map
<wxMenuItem
*, wxMenu
*> menu_by_item
;
297 std::map
<std::string
, wxMenu
*> submenu_by_name
;
298 std::map
<std::string
, wxMenuItem
*> submenui_by_name
;
299 std::set
<unsigned> toggles
;
303 wxMenu
* system_menu::lookup_menu(const std::string
& key
)
307 if(submenu_by_name
.count(key
))
308 return submenu_by_name
[key
];
311 sep
= AppendSeparator();
312 auto p
= lsplit(key
);
313 wxMenu
* into
= lookup_menu(p
.first
);
314 submenu_by_name
[key
] = new wxMenu();
315 submenui_by_name
[key
] = into
->AppendSubMenu(submenu_by_name
[key
], towxstring(p
.second
));
316 menu_by_item
[submenui_by_name
[key
]] = into
;
317 return submenu_by_name
[key
];
320 void system_menu::insert_act(unsigned id
, const std::string
& label
, bool dots
, bool check
)
323 sep
= AppendSeparator();
325 auto p
= lsplit(label
);
326 wxMenu
* into
= lookup_menu(p
.first
);
328 action_by_id
[next_id
] = id
;
329 std::string use_label
= p
.second
+ (dots
? "..." : "");
331 item_by_action
[id
] = into
->AppendCheckItem(next_id
, towxstring(use_label
));
334 item_by_action
[id
] = into
->Append(next_id
, towxstring(use_label
));
335 menu_by_item
[item_by_action
[id
]] = into
;
336 pwin
->Connect(next_id
++, wxEVT_COMMAND_MENU_SELECTED
,
337 wxCommandEventHandler(system_menu::on_select
), NULL
, this);
340 void system_menu::insert_pass(int id
, const std::string
& label
)
342 pwin
->Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
343 wxCommandEventHandler(wxwin_mainwindow::handle_menu_click
), NULL
, pwin
);
344 Append(id
, towxstring(label
));
347 system_menu::system_menu(wxWindow
* win
)
350 insert_pass(wxID_PAUSE
, "Pause/Unpause");
351 insert_pass(wxID_FRAMEADVANCE
, "Step frame");
352 insert_pass(wxID_SUBFRAMEADVANCE
, "Step subframe");
353 insert_pass(wxID_NEXTPOLL
, "Step poll");
357 system_menu::~system_menu()
361 void system_menu::on_select(wxCommandEvent
& e
)
363 if(!action_by_id
.count(e
.GetId()))
365 unsigned act_id
= action_by_id
[e
.GetId()];
366 const interface_action
* act
= NULL
;
367 for(auto i
: our_rom
.rtype
->get_actions())
368 if(i
->id
== act_id
) {
375 auto p
= prompt_action_params(pwin
, act
->get_title(), act
->params
);
376 runemufn([act_id
,p
]() { our_rom
.rtype
->execute_action(act_id
, p
); });
377 } catch(canceled_exception
& e
) {
378 } catch(std::bad_alloc
& e
) {
383 void system_menu::update(bool light
)
386 next_id
= wxID_ACTIONS_FIRST
;
391 for(auto i
= item_by_action
.begin(); i
!= item_by_action
.end(); i
++)
392 menu_by_item
[i
->second
]->Destroy(i
->second
);
393 for(auto i
= submenui_by_name
.rbegin(); i
!= submenui_by_name
.rend(); i
++)
394 menu_by_item
[i
->second
]->Destroy(i
->second
);
395 action_by_id
.clear();
396 item_by_action
.clear();
397 menu_by_item
.clear();
398 submenu_by_name
.clear();
399 submenui_by_name
.clear();
402 for(auto i
: our_rom
.rtype
->get_actions())
403 insert_act(i
->id
, i
->get_title(), !i
->params
.empty(), i
->is_toggle());
405 for(auto i
: item_by_action
)
406 i
.second
->Enable(our_rom
.rtype
->action_flags(i
.first
) & 1);
407 for(auto i
: toggles
)
408 item_by_action
[i
]->Check(our_rom
.rtype
->action_flags(i
) & 2);
411 std::string
munge_name(const std::string
& orig
)
415 if(r
= regex("(.*)\\(([0-9]+)\\)", newname
)) {
418 sequence
= parse_value
<uint64_t>(r
[2]);
419 newname
= (stringfmt() << r
[1] << "(" << sequence
+ 1 << ")").str();
421 newname
= newname
+ "(2)";
424 newname
= newname
+ "(2)";
429 void handle_watch_load(std::map
<std::string
, std::string
>& new_watches
, std::set
<std::string
>& old_watches
)
431 auto proj
= project_get();
433 for(auto i
: new_watches
) {
434 std::string name
= i
.first
;
436 if(!old_watches
.count(name
)) {
438 if(name
!= "" && i
.second
!= "")
439 lsnes_memorywatch
.set(name
, i
.second
);
440 } catch(std::exception
& e
) {
441 messages
<< "Can't set memory watch '" << name
<< "': "
442 << e
.what() << std::endl
;
445 } else if(lsnes_memorywatch
.get_string(name
) == i
.second
)
448 name
= munge_name(name
);
452 for(auto i
: new_watches
)
454 if(i
.first
!= "" && i
.second
!= "")
455 lsnes_memorywatch
.set(i
.first
, i
.second
);
456 } catch(std::exception
& e
) {
457 messages
<< "Can't set memory watch '" << i
.first
<< "': "
458 << e
.what() << std::endl
;
460 for(auto i
: old_watches
)
461 if(!new_watches
.count(i
))
463 lsnes_memorywatch
.clear(i
);
464 } catch(std::exception
& e
) {
465 messages
<< "Can't clear memory watch '" << i
<< "': "
466 << e
.what() << std::endl
;
471 std::string
get_default_screenshot_name()
473 auto p
= project_get();
477 auto files
= enumerate_directory(p
->directory
, ".*-[0-9]+\\.png");
478 std::set
<std::string
> numbers
;
479 for(auto i
: files
) {
482 split
= i
.find_last_of("\\/");
484 split
= i
.find_last_of("/");
486 std::string name
= i
;
487 if(split
< name
.length())
488 name
= name
.substr(split
+ 1);
489 regex_results r
= regex("(.*)-([0-9]+)\\.png", name
);
490 if(r
[1] != p
->prefix
)
492 numbers
.insert(r
[2]);
494 for(uint64_t i
= 1;; i
++) {
495 std::string candidate
= (stringfmt() << i
).str();
496 if(!numbers
.count(candidate
))
497 return p
->prefix
+ "-" + candidate
+ ".png";
502 std::string
project_prefixname(const std::string ext
)
504 auto p
= project_get();
508 return p
->prefix
+ "." + ext
;
512 double pick_volume(wxWindow
* win
, const std::string
& title
, std::string
& last
)
517 value
= pick_text(win
, title
, "Enter volume in absolute units, percentage (%) or dB:",
519 if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)", value
))
520 parsed
= strtod(r
[1].c_str(), NULL
);
521 else if(r
= regex("([0-9]*\\.[0-9]+|[0-9]+)%", value
))
522 parsed
= strtod(r
[1].c_str(), NULL
) / 100;
523 else if(r
= regex("([+-]?([0-9]*.[0-9]+|[0-9]+))dB", value
))
524 parsed
= pow(10, strtod(r
[1].c_str(), NULL
) / 20);
526 wxMessageBox(wxT("Invalid volume"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, win
);
533 void recent_rom_selected(const recentfiles::multirom
& file
)
536 req
.packfile
= file
.packfile
;
537 req
.singlefile
= file
.singlefile
;
538 req
.core
= file
.core
;
539 req
.system
= file
.system
;
540 req
.region
= file
.region
;
541 for(unsigned i
= 0; i
< file
.files
.size() && i
< ROM_SLOT_COUNT
; i
++)
542 req
.files
[i
] = file
.files
[i
];
543 runemufn_async([req
]() {
544 lsnes_cmd
.invoke("unpause-emulator");
549 void recent_movie_selected(const recentfiles::path
& file
)
551 platform::queue("load-smart " + file
.get_path());
554 void recent_script_selected(const recentfiles::path
& file
)
556 platform::queue("run-lua " + file
.get_path());
561 std::string windowname
= "lsnes rr" + lsnes_version
+ " [";
562 auto p
= project_get();
564 windowname
= windowname
+ p
->name
;
566 windowname
= windowname
+ our_rom
.rtype
->get_core_identifier();
567 windowname
= windowname
+ "]";
568 return towxstring(windowname
);
573 struct loaded_rom rom
;
574 struct moviefile
* initial
;
575 bool load_has_to_succeed
;
578 void* emulator_main(void* _args
)
580 struct emu_args
* args
= reinterpret_cast<struct emu_args
*>(_args
);
583 messages
<< "Using core: " << our_rom
.rtype
->get_core_identifier() << std::endl
;
584 struct moviefile
* movie
= args
->initial
;
585 bool has_to_succeed
= args
->load_has_to_succeed
;
586 platform::flush_command_queue();
587 main_loop(our_rom
, *movie
, has_to_succeed
);
588 signal_program_exit();
589 } catch(std::bad_alloc
& e
) {
591 } catch(std::exception
& e
) {
592 messages
<< "FATAL: " << e
.what() << std::endl
;
593 platform::fatal_error();
599 void join_emulator_thread()
601 emulation_thread
->join();
604 keyboard::mouse_calibration mouse_cal
= {0};
605 keyboard::key_mouse
mouse_x(lsnes_kbd
, "mouse_x", "mouse", mouse_cal
);
606 keyboard::key_mouse
mouse_y(lsnes_kbd
, "mouse_y", "mouse", mouse_cal
);
607 keyboard::key_key
mouse_l(lsnes_kbd
, "mouse_left", "mouse");
608 keyboard::key_key
mouse_m(lsnes_kbd
, "mouse_center", "mouse");
609 keyboard::key_key
mouse_r(lsnes_kbd
, "mouse_right", "mouse");
610 keyboard::key_key
mouse_i(lsnes_kbd
, "mouse_inwindow", "mouse");
612 std::pair
<double, double> calc_scale_factors(double factor
, bool ar
,
616 return std::make_pair(factor
, factor
);
618 //Too wide, make taller.
619 return std::make_pair(factor
, factor
/ par
);
621 //Too narrow, make wider.
622 return std::make_pair(factor
* par
, factor
);
626 void handle_wx_mouse(wxMouseEvent
& e
)
628 auto sfactors
= calc_scale_factors(video_scale_factor
, arcorrect_enabled
,
629 (our_rom
.rtype
) ? our_rom
.rtype
->get_PAR() : 1.0);
630 platform::queue(keypress(keyboard::modifier_set(), mouse_x
, e
.GetX() / sfactors
.first
));
631 platform::queue(keypress(keyboard::modifier_set(), mouse_y
, e
.GetY() / sfactors
.second
));
633 platform::queue(keypress(keyboard::modifier_set(), mouse_i
, 1));
635 platform::queue(keypress(keyboard::modifier_set(), mouse_i
, 0));
637 platform::queue(keypress(keyboard::modifier_set(), mouse_l
, 1));
639 platform::queue(keypress(keyboard::modifier_set(), mouse_l
, 0));
641 platform::queue(keypress(keyboard::modifier_set(), mouse_m
, 1));
643 platform::queue(keypress(keyboard::modifier_set(), mouse_m
, 0));
645 platform::queue(keypress(keyboard::modifier_set(), mouse_r
, 1));
647 platform::queue(keypress(keyboard::modifier_set(), mouse_r
, 0));
650 bool is_readonly_mode()
653 runemufn([&ret
]() { ret
= movb
? movb
.get_movie().readonly_mode() : false; });
657 std::pair
<int, int> UI_controller_index_by_logical(unsigned lid
)
659 std::pair
<int, int> ret
;
660 runemufn([&ret
, lid
]() { ret
= controls
.lcid_to_pcid(lid
); });
664 void set_speed(double target
)
667 set_speed_multiplier(std::numeric_limits
<double>::infinity());
669 set_speed_multiplier(target
/ 100);
672 void update_preferences()
674 preferred_core
.clear();
675 for(auto i
: core_type::get_core_types()) {
676 std::string val
= i
->get_hname() + " / " + i
->get_core_identifier();
677 for(auto j
: i
->get_extensions()) {
678 std::string key
= "ext:" + j
;
679 if(core_selections
.count(key
) && core_selections
[key
] == val
)
680 preferred_core
[key
] = i
;
682 std::string key2
= "type:" + i
->get_iname();
683 if(core_selections
.count(key2
) && core_selections
[key2
] == val
)
684 preferred_core
[key2
] = i
;
688 std::string
movie_path()
690 return lsnes_vset
["moviepath"].str();
693 bool is_lsnes_movie(const std::string
& filename
)
695 std::istream
* s
= NULL
;
698 s
= &zip::openrel(filename
, "");
701 if(*s
&& !strcmp(buf
, "lsmv\x1A"))
709 zip::reader
r(filename
);
710 std::istream
& s
= r
["systemid"];
715 return (s2
== "lsnes-rr1");
721 class loadfile
: public wxFileDropTarget
724 loadfile(wxwin_mainwindow
* win
) : pwin(win
) {};
725 bool OnDropFiles(wxCoord x
, wxCoord y
, const wxArrayString
& filenames
)
728 if(filenames
.Count() == 2) {
729 std::string a
= tostdstring(filenames
[0]);
730 std::string b
= tostdstring(filenames
[1]);
731 bool amov
= is_lsnes_movie(a
);
732 bool bmov
= is_lsnes_movie(b
);
735 if(amov
) std::swap(a
, b
);
736 runemufn_async([a
, b
]() {
737 lsnes_cmd
.invoke("unpause-emulator");
741 lsnes_cmd
.invoke("load-smart " + b
);
745 if(filenames
.Count() == 1) {
746 std::string a
= tostdstring(filenames
[0]);
747 bool amov
= is_lsnes_movie(a
);
749 platform::queue("load-smart " + a
);
750 pwin
->recent_movies
->add(a
);
755 runemufn_async([req
]() {
756 lsnes_cmd
.invoke("unpause-emulator");
759 pwin
->recent_roms
->add(loadreq_to_multirom(req
));
765 wxwin_mainwindow
* pwin
;
769 void boot_emulator(loaded_rom
& rom
, moviefile
& movie
, bool fscreen
)
771 update_preferences();
773 struct emu_args
* a
= new emu_args
;
776 a
->load_has_to_succeed
= false;
777 modal_pause_holder hld
;
778 emulation_thread
= new thread_class(emulator_main
, a
);
779 main_window
= new wxwin_mainwindow(fscreen
);
781 } catch(std::bad_alloc
& e
) {
786 wxwin_mainwindow::panel::panel(wxWindow
* win
)
787 : wxPanel(win
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
)
789 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(panel::on_paint
), NULL
, this);
790 this->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(panel::on_erase
), NULL
, this);
791 this->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(panel::on_keyboard_down
), NULL
, this);
792 this->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(panel::on_keyboard_up
), NULL
, this);
793 this->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
794 this->Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
795 this->Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
796 this->Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
797 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
798 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
799 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
800 this->Connect(wxEVT_ENTER_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
801 this->Connect(wxEVT_LEAVE_WINDOW
, wxMouseEventHandler(panel::on_mouse
), NULL
, this);
802 SetMinSize(wxSize(512, 448));
805 void wxwin_mainwindow::menu_start(wxString name
)
807 while(!upper
.empty())
809 current_menu
= new wxMenu();
810 menubar
->Append(current_menu
, name
);
813 void wxwin_mainwindow::menu_special(wxString name
, wxMenu
* menu
)
815 while(!upper
.empty())
817 menubar
->Append(menu
, name
);
821 wxMenuItem
* wxwin_mainwindow::menu_special_sub(wxString name
, wxMenu
* menu
)
823 return current_menu
->AppendSubMenu(menu
, name
);
826 void wxwin_mainwindow::menu_entry(int id
, wxString name
)
828 current_menu
->Append(id
, name
);
829 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
830 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
833 void wxwin_mainwindow::menu_entry_check(int id
, wxString name
)
835 checkitems
[id
] = current_menu
->AppendCheckItem(id
, name
);
836 Connect(id
, wxEVT_COMMAND_MENU_SELECTED
,
837 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click
), NULL
, this);
840 void wxwin_mainwindow::menu_start_sub(wxString name
)
842 wxMenu
* old
= current_menu
;
843 upper
.push(current_menu
);
844 current_menu
= new wxMenu();
845 old
->AppendSubMenu(current_menu
, name
);
848 void wxwin_mainwindow::menu_end_sub()
850 current_menu
= upper
.top();
854 bool wxwin_mainwindow::menu_ischecked(int id
)
856 if(checkitems
.count(id
))
857 return checkitems
[id
]->IsChecked();
862 void wxwin_mainwindow::menu_check(int id
, bool newstate
)
864 if(checkitems
.count(id
))
865 return checkitems
[id
]->Check(newstate
);
870 void wxwin_mainwindow::menu_enable(int id
, bool newstate
)
872 auto item
= menubar
->FindItem(id
);
875 item
->Enable(newstate
);
878 void wxwin_mainwindow::menu_separator()
880 current_menu
->AppendSeparator();
883 void wxwin_mainwindow::panel::request_paint()
888 void wxwin_mainwindow::panel::on_paint(wxPaintEvent
& e
)
890 if(wx_escape_count
>= 3 && is_fs
) {
891 //Leave fullscreen mode.
892 main_window
->enter_or_leave_fullscreen(false);
894 render_framebuffer();
902 bool aux
= hflip_enabled
|| vflip_enabled
|| rotate_enabled
;
903 auto sfactors
= calc_scale_factors(video_scale_factor
, arcorrect_enabled
, our_rom
.rtype
?
904 our_rom
.rtype
->get_PAR() : 1.0);
906 tw
= main_screen
.get_height() * sfactors
.second
+ 0.5;
907 th
= main_screen
.get_width() * sfactors
.first
+ 0.5;
909 tw
= main_screen
.get_width() * sfactors
.first
+ 0.5;
910 th
= main_screen
.get_height() * sfactors
.second
+ 0.5;
913 main_window_dirty
= false;
916 //Scale this to fullscreen.
918 wxSize screen
= main_window
->GetSize();
919 double fss
= min(1.0 * screen
.GetWidth() / tw
, 1.0 * screen
.GetHeight() / th
);
924 if(!screen_buffer
|| tw
!= old_width
|| th
!= old_height
|| scaling_flags
!= old_flags
||
925 hflip_enabled
!= old_hflip
|| vflip_enabled
!= old_vflip
|| rotate_enabled
!= old_rotate
) {
927 delete[] screen_buffer
;
928 screen_buffer
= NULL
;
931 delete[] rotate_buffer
;
932 rotate_buffer
= NULL
;
936 old_flags
= scaling_flags
;
937 old_hflip
= hflip_enabled
;
938 old_vflip
= vflip_enabled
;
939 old_rotate
= rotate_enabled
;
940 uint32_t w
= main_screen
.get_width();
941 uint32_t h
= main_screen
.get_height();
943 sws_ctx
= sws_getCachedContext(sws_ctx
, rotate_enabled
? h
: w
, rotate_enabled
? w
: h
,
944 PIX_FMT_RGBA
, tw
, th
, PIX_FMT_BGR24
, scaling_flags
, NULL
, NULL
, NULL
);
945 tw
= max(tw
, static_cast<uint32_t>(128));
946 th
= max(th
, static_cast<uint32_t>(112));
947 screen_buffer
= new unsigned char[tw
* th
* 3];
949 rotate_buffer
= new uint32_t[main_screen
.get_width() * main_screen
.get_height()];
950 SetMinSize(wxSize(tw
, th
));
951 signal_resize_needed();
954 //Hflip, Vflip or rotate active.
955 size_t width
= main_screen
.get_width();
956 size_t height
= main_screen
.get_height();
957 size_t width1
= width
- 1;
958 size_t height1
= height
- 1;
959 size_t stride
= main_screen
.rowptr(1) - main_screen
.rowptr(0);
960 uint32_t* pixels
= main_screen
.rowptr(0);
962 for(unsigned y
= 0; y
< height
; y
++) {
963 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
964 uint32_t* dpixels
= rotate_buffer
+ (height1
- y
);
966 for(unsigned x
= 0; x
< width
; x
++)
967 dpixels
[x
* height
] = pixels2
[width1
- x
];
969 for(unsigned x
= 0; x
< width
; x
++)
970 dpixels
[x
* height
] = pixels2
[x
];
973 for(unsigned y
= 0; y
< height
; y
++) {
974 uint32_t* pixels2
= pixels
+ (vflip_enabled
? (height1
- y
) : y
) * stride
;
975 uint32_t* dpixels
= rotate_buffer
+ y
* width
;
977 for(unsigned x
= 0; x
< width
; x
++)
978 dpixels
[x
] = pixels2
[width1
- x
];
980 for(unsigned x
= 0; x
< width
; x
++)
981 dpixels
[x
] = pixels2
[x
];
986 srcs
[0] = 4 * (rotate_enabled
? main_screen
.get_height() : main_screen
.get_width());
988 srcs
[0] = 4 * main_screen
.get_stride();
990 srcp
[0] = reinterpret_cast<unsigned char*>(aux
? rotate_buffer
: main_screen
.rowptr(0));
991 dstp
[0] = screen_buffer
;
992 memset(screen_buffer
, 0, tw
* th
* 3);
993 if(main_screen
.get_width() && main_screen
.get_height())
994 sws_scale(sws_ctx
, srcp
, srcs
, 0, rotate_enabled
? main_screen
.get_width() : main_screen
.get_height(),
996 wxBitmap
bmp(wxImage(tw
, th
, screen_buffer
, true));
997 dc
.DrawBitmap(bmp
, 0, 0, false);
998 main_window_dirty
= false;
1001 void wxwin_mainwindow::panel::on_erase(wxEraseEvent
& e
)
1006 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent
& e
)
1008 handle_wx_keyboard(e
, true);
1011 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent
& e
)
1013 handle_wx_keyboard(e
, false);
1016 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent
& e
)
1021 wxwin_mainwindow::wxwin_mainwindow(bool fscreen
)
1022 : wxFrame(NULL
, wxID_ANY
, getname(), wxDefaultPosition
, wxSize(-1, -1),
1023 wxMINIMIZE_BOX
| wxSYSTEM_MENU
| wxCAPTION
| wxCLIP_CHILDREN
| wxCLOSE_BOX
)
1025 download_in_progress
= NULL
;
1028 toplevel
= new wxFlexGridSizer(1, 2, 0, 0);
1029 toplevel
->Add(gpanel
= new panel(this), 1, wxGROW
);
1030 toplevel
->Add(spanel
= new wxwin_status::panel(this, gpanel
, 20), 1, wxGROW
);
1031 spanel_shown
= true;
1032 toplevel
->SetSizeHints(this);
1036 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_mainwindow::on_close
));
1037 SetMenuBar(menubar
= new wxMenuBar
);
1038 SetStatusBar(statusbar
= new wxStatusBar(this));
1040 menu_start(wxT("File"));
1041 menu_start_sub(wxT("New"));
1042 menu_entry(wxID_NEW_MOVIE
, wxT("Movie..."));
1043 menu_entry(wxID_NEW_PROJECT
, wxT("Project..."));
1045 menu_start_sub(wxT("Load"));
1046 menu_entry(wxID_LOAD_STATE
, wxT("State..."));
1047 menu_entry(wxID_LOAD_MOVIE
, wxT("Movie..."));
1048 menu_entry(wxID_DOWNLOAD
, wxT("Download movie..."));
1049 if(loadlib::library::name() != "") {
1051 menu_entry(wxID_LOAD_LIBRARY
, towxstring(std::string("Load ") + loadlib::library::name()));
1052 menu_entry(wxID_PLUGIN_MANAGER
, towxstring("Plugin manager"));
1055 menu_entry(wxID_RELOAD_ROM_IMAGE
, wxT("Reload ROM"));
1056 menu_entry(wxID_LOAD_ROM_IMAGE_FIRST
, wxT("ROM..."));
1057 menu_special_sub(wxT("Multifile ROM"), loadroms
= new loadrom_menu(this, wxID_LOAD_ROM_IMAGE_FIRST
+ 1,
1058 wxID_LOAD_ROM_IMAGE_LAST
, [this](core_type
* t
) { this->do_load_rom_image(t
); }));
1059 menu_special_sub(wxT("Project"), projects
= new projects_menu(this, wxID_PROJECT_FIRST
, wxID_PROJECT_LAST
,
1060 get_config_path() + "/recent-projects.txt", [this](const std::string
& id
) {
1061 this->project_selected(id
); }));
1063 menu_special_sub(wxT("Recent ROMs"), recent_roms
= new recent_menu
<recentfiles::multirom
>(this,
1064 wxID_RROM_FIRST
, wxID_RROM_LAST
, get_config_path() + "/recent-roms.txt", recent_rom_selected
));
1065 menu_special_sub(wxT("Recent Movies"), recent_movies
= new recent_menu
<recentfiles::path
>(this,
1066 wxID_RMOVIE_FIRST
, wxID_RMOVIE_LAST
, get_config_path() + "/recent-movies.txt",
1067 recent_movie_selected
));
1068 menu_special_sub(wxT("Recent Lua scripts"), recent_scripts
= new recent_menu
<recentfiles::path
>(this,
1069 wxID_RLUA_FIRST
, wxID_RLUA_LAST
, get_config_path() + "/recent-scripts.txt",
1070 recent_script_selected
));
1072 menu_entry(wxID_CONFLICTRESOLUTION
, wxT("Conflict resolution"));
1074 branches_menu
* brlist
;
1075 auto brlist_item
= menu_special_sub(wxT("Branches"), brlist
= new branches_menu(this, wxID_BRANCH_FIRST
,
1077 brlist
->set_disabler([brlist_item
](bool enabled
) { brlist_item
->Enable(enabled
); });
1080 menu_start_sub(wxT("Save"));
1081 menu_entry(wxID_SAVE_STATE
, wxT("State..."));
1082 menu_entry(wxID_SAVE_MOVIE
, wxT("Movie..."));
1083 menu_entry(wxID_SAVE_SCREENSHOT
, wxT("Screenshot..."));
1084 menu_entry(wxID_SAVE_SUBTITLES
, wxT("Subtitles..."));
1085 menu_entry(wxID_CANCEL_SAVES
, wxT("Cancel pending saves"));
1087 menu_entry(wxID_CHDIR
, wxT("Change working directory..."));
1089 menu_special_sub(wxT("Upload"), new upload_menu(this, wxID_UPLOAD_FIRST
, wxID_UPLOAD_LAST
));
1091 menu_start_sub(wxT("Close"));
1092 menu_entry(wxID_CLOSE_PROJECT
, wxT("Project"));
1093 menu_entry(wxID_CLOSE_ROM
, wxT("ROM"));
1094 menu_enable(wxID_CLOSE_PROJECT
, project_get() != NULL
);
1095 menu_enable(wxID_CLOSE_ROM
, project_get() == NULL
);
1098 menu_entry(wxID_EXIT
, wxT("Quit"));
1100 menu_special(wxT("System"), reinterpret_cast<wxMenu
*>(sysmenu
= new system_menu(this)));
1102 menu_start(wxT("Movie"));
1103 menu_entry_check(wxID_READONLY_MODE
, wxT("Readonly mode"));
1104 menu_check(wxID_READONLY_MODE
, is_readonly_mode());
1105 menu_entry(wxID_EDIT_AUTHORS
, wxT("Edit game name && authors..."));
1106 menu_entry(wxID_EDIT_SUBTITLES
, wxT("Edit subtitles..."));
1107 menu_entry(wxID_EDIT_VSUBTITLES
, wxT("Edit commantary track..."));
1109 menu_entry(wxID_REWIND_MOVIE
, wxT("Rewind to start"));
1111 menu_start(wxT("Speed"));
1112 menu_entry(wxID_SPEED_5
, wxT("1/20x"));
1113 menu_entry(wxID_SPEED_10
, wxT("1/10x"));
1114 menu_entry(wxID_SPEED_17
, wxT("1/6x"));
1115 menu_entry(wxID_SPEED_20
, wxT("1/5x"));
1116 menu_entry(wxID_SPEED_25
, wxT("1/4x"));
1117 menu_entry(wxID_SPEED_33
, wxT("1/3x"));
1118 menu_entry(wxID_SPEED_50
, wxT("1/2x"));
1119 menu_entry(wxID_SPEED_100
, wxT("1x"));
1120 menu_entry(wxID_SPEED_150
, wxT("1.5x"));
1121 menu_entry(wxID_SPEED_200
, wxT("2x"));
1122 menu_entry(wxID_SPEED_300
, wxT("3x"));
1123 menu_entry(wxID_SPEED_500
, wxT("5x"));
1124 menu_entry(wxID_SPEED_1000
, wxT("10x"));
1125 menu_entry(wxID_SPEED_TURBO
, wxT("Turbo"));
1126 menu_entry(wxID_SET_SPEED
, wxT("Set..."));
1128 menu_start(wxT("Tools"));
1129 menu_entry(wxID_RUN_SCRIPT
, wxT("Run batch file..."));
1131 menu_entry(wxID_EVAL_LUA
, wxT("Evaluate Lua statement..."));
1132 menu_entry(wxID_RUN_LUA
, wxT("Run Lua script..."));
1134 menu_entry(wxID_RESET_LUA
, wxT("Reset Lua VM"));
1136 menu_entry(wxID_AUTOHOLD
, wxT("Autohold/Autofire..."));
1137 menu_entry(wxID_TASINPUT
, wxT("TAS input plugin..."));
1138 menu_entry(wxID_MULTITRACK
, wxT("Multitrack..."));
1139 menu_entry(wxID_EDIT_MACROS
, wxT("Edit macros..."));
1141 menu_entry(wxID_EDIT_MEMORYWATCH
, wxT("Edit memory watch..."));
1143 menu_entry(wxID_LOAD_MEMORYWATCH
, wxT("Load memory watch..."));
1144 menu_entry(wxID_SAVE_MEMORYWATCH
, wxT("Save memory watch..."));
1146 menu_entry(wxID_MEMORY_SEARCH
, wxT("Memory Search..."));
1147 menu_entry(wxID_HEXEDITOR
, wxT("Memory editor..."));
1148 tracelog_menu
* trlog
;
1149 auto trlog_item
= menu_special_sub(wxT("Trace log"), trlog
= new tracelog_menu(this, wxID_TRACELOG_FIRST
,
1150 wxID_TRACELOG_LAST
));
1151 trlog
->set_disabler([trlog_item
](bool enabled
) { trlog_item
->Enable(enabled
); });
1153 menu_entry(wxID_DISASSEMBLER
, wxT("Disassembler..."));
1155 menu_entry(wxID_MOVIE_EDIT
, wxT("Edit movie..."));
1157 menu_special_sub(wxT("Video Capture"), reinterpret_cast<dumper_menu
*>(dmenu
= new dumper_menu(this,
1158 wxID_DUMP_FIRST
, wxID_DUMP_LAST
)));
1160 menu_start(wxT("Configure"));
1161 menu_entry_check(wxID_SHOW_STATUS
, wxT("Show status panel"));
1162 menu_check(wxID_SHOW_STATUS
, true);
1163 menu_entry_check(wxID_DEDICATED_MEMORY_WATCH
, wxT("Dedicated memory watch"));
1164 menu_entry(wxID_SHOW_MESSAGES
, wxT("Show messages"));
1165 menu_special_sub(wxT("Settings"), new settings_menu(this, wxID_SETTINGS_FIRST
));
1166 if(audioapi_driver_initialized()) {
1168 menu_entry_check(wxID_AUDIO_ENABLED
, wxT("Sounds enabled"));
1169 menu_entry(wxID_VUDISPLAY
, wxT("VU display / sound controls"));
1170 menu_check(wxID_AUDIO_ENABLED
, platform::is_sound_enabled());
1173 menu_entry(wxID_ENTER_FULLSCREEN
, wxT("Enter fullscreen mode"));
1175 menu_start(wxT("Help"));
1176 menu_entry(wxID_ABOUT
, wxT("About..."));
1178 corechange
.set(notify_core_change
, []() { signal_core_change(); });
1179 titlechange
.set(notify_title_change
, []() { signal_core_change(); });
1180 newcore
.set(notify_new_core
, []() { update_preferences(); });
1181 unmuted
.set(notify_sound_unmute
, [this](bool unmute
) {
1182 runuifun([this, unmute
]() { this->menu_check(wxID_AUDIO_ENABLED
, unmute
); });
1184 modechange
.set(notify_mode_change
, [this](bool readonly
) {
1185 runuifun([this, readonly
]() { this->menu_check(wxID_READONLY_MODE
, readonly
); });
1187 gpanel
->SetDropTarget(new loadfile(this));
1188 spanel
->SetDropTarget(new loadfile(this));
1189 set_hasher_callback(hash_callback
);
1190 reinterpret_cast<system_menu
*>(sysmenu
)->update(false);
1191 menubar
->SetMenuLabel(1, towxstring(our_rom
.rtype
->get_systemmenu_name()));
1192 focus_timer
= new _focus_timer
;
1194 wx_escape_count
= 0;
1195 enter_or_leave_fullscreen(true);
1199 wxwin_mainwindow::~wxwin_mainwindow()
1201 if(sws_ctx
) sws_freeContext(sws_ctx
);
1202 if(screen_buffer
) delete[] screen_buffer
;
1203 if(rotate_buffer
) delete[] rotate_buffer
;
1206 void wxwin_mainwindow::request_paint()
1211 void wxwin_mainwindow::on_close(wxCloseEvent
& e
)
1213 //Veto it for now, latter things will delete it.
1215 platform::queue("quit-emulator");
1218 void wxwin_mainwindow::notify_update() throw()
1220 if(!main_window_dirty
) {
1221 main_window_dirty
= true;
1226 void wxwin_mainwindow::notify_resized() throw()
1229 toplevel
->SetSizeHints(this);
1233 void wxwin_mainwindow::notify_update_status() throw()
1235 spanel
->request_paint();
1237 mwindow
->notify_update();
1240 void wxwin_mainwindow::notify_exit() throw()
1242 wxwidgets_exiting
= true;
1243 join_emulator_thread();
1247 std::u32string
read_variable_map(const std::map
<std::string
, std::u32string
>& vars
, const std::string
& key
)
1249 if(!vars
.count(key
))
1251 return vars
.find(key
)->second
;
1254 void wxwin_mainwindow::update_statusbar(const std::map
<std::string
, std::u32string
>& vars
)
1256 if(download_in_progress
) {
1257 statusbar
->SetStatusText(towxstring(download_in_progress
->statusmsg()));
1260 if(hashing_in_progress
) {
1261 //TODO: Display this as a dialog.
1262 std::ostringstream s
;
1263 s
<< "Hashing ROMs, approximately " << ((hashing_left
+ 524288) >> 20) << " of "
1264 << ((hashing_total
+ 524288) >> 20) << "MB left...";
1265 statusbar
->SetStatusText(towxstring(s
.str()));
1271 std::basic_ostringstream
<char32_t
> s
;
1272 bool recording
= (read_variable_map(vars
, "!mode") == U
"R");
1274 s
<< U
"Frame: " << read_variable_map(vars
, "!frame");
1276 s
<< U
"Frame: " << read_variable_map(vars
, "!frame") << U
"/" <<
1277 read_variable_map(vars
, "!length");
1278 s
<< U
" Lag: " << read_variable_map(vars
, "!lag");
1279 s
<< U
" Subframe: " << read_variable_map(vars
, "!subframe");
1280 if(vars
.count("!saveslot")) {
1282 if(vars
.count("!branch")) s
<< read_variable_map(vars
, "!branch") << U
"ā";
1283 s
<< read_variable_map(vars
, "!saveslot");
1285 if(vars
.count("!saveslotinfo"))
1286 s
<< U
" [" << read_variable_map(vars
, "!saveslotinfo") << U
"]";
1287 s
<< U
" Speed: " << read_variable_map(vars
, "!speed") << U
"%";
1289 if(read_variable_map(vars
, "!pause") == U
"B")
1290 s
<< U
" Breakpoint";
1291 else if(read_variable_map(vars
, "!pause") == U
"P")
1293 if(read_variable_map(vars
, "!dumping") != U
"")
1295 if(read_variable_map(vars
, "!mode") == U
"C")
1297 else if(read_variable_map(vars
, "!mode") == U
"R")
1299 else if(read_variable_map(vars
, "!mode") == U
"P")
1301 else if(read_variable_map(vars
, "!mode") == U
"F")
1305 if(vars
.count("!mbranch"))
1306 s
<< U
" Branch: " << read_variable_map(vars
, "!mbranch");
1307 std::u32string macros
= read_variable_map(vars
, "!macros");
1309 s
<< U
" Macros: " << macros
;
1311 statusbar
->SetStatusText(towxstring(s
.str()));
1312 } catch(std::exception
& e
) {
1316 #define NEW_KEYBINDING "A new binding..."
1317 #define NEW_ALIAS "A new alias..."
1318 #define NEW_WATCH "A new watch..."
1320 void wxwin_mainwindow::handle_menu_click(wxCommandEvent
& e
)
1323 handle_menu_click_cancelable(e
);
1324 } catch(canceled_exception
& e
) {
1326 } catch(std::bad_alloc
& e
) {
1328 } catch(std::exception
& e
) {
1329 show_message_ok(this, "Error in menu handler", e
.what(), wxICON_EXCLAMATION
);
1333 void wxwin_mainwindow::refresh_title() throw()
1335 SetTitle(getname());
1336 auto p
= project_get();
1337 menu_enable(wxID_RELOAD_ROM_IMAGE
, !p
);
1338 for(int i
= wxID_LOAD_ROM_IMAGE_FIRST
; i
<= wxID_LOAD_ROM_IMAGE_LAST
; i
++)
1340 menu_enable(wxID_CLOSE_PROJECT
, p
!= NULL
);
1341 menu_enable(wxID_CLOSE_ROM
, p
== NULL
);
1342 reinterpret_cast<system_menu
*>(sysmenu
)->update(false);
1343 menubar
->SetMenuLabel(1, towxstring(our_rom
.rtype
->get_systemmenu_name()));
1348 struct movie_or_savestate
1351 typedef std::pair
<std::string
,std::string
> returntype
;
1352 movie_or_savestate(bool is_state
)
1356 filedialog_input_params
input(bool save
) const
1358 filedialog_input_params p
;
1359 std::string ext
= state
? project_savestate_ext() : "lsmv";
1360 std::string name
= state
? "Savestates" : "Movies";
1362 p
.types
.push_back(filedialog_type_entry(name
, "*." + ext
, ext
));
1363 p
.types
.push_back(filedialog_type_entry(name
+ " (binary)", "*." + ext
, ext
));
1365 p
.types
.push_back(filedialog_type_entry(name
, "*." + ext
+ ";*." + ext
+ ".backup",
1367 if(!save
&& state
) {
1368 p
.types
.push_back(filedialog_type_entry("Savestates [read only]", "*." + ext
+
1369 ";*." + ext
+ ".backup", ext
));
1370 p
.types
.push_back(filedialog_type_entry("Savestates [read-write]", "*." + ext
+
1371 ";*." + ext
+ ".backup", ext
));
1372 p
.types
.push_back(filedialog_type_entry("Savestates [preserve]", "*." + ext
+
1373 ";*." + ext
+ ".backup", ext
));
1374 p
.types
.push_back(filedialog_type_entry("Savestates [all branches]", "*." + ext
+
1375 ";*." + ext
+ ".backup", ext
));
1377 p
.default_type
= save
? (state
? save_dflt_binary
: movie_dflt_binary
) : 0;
1380 std::pair
<std::string
, std::string
> output(const filedialog_output_params
& p
, bool save
) const
1384 cmdmod
= p
.typechoice
? "-binary" : "-zip";
1386 switch(p
.typechoice
) {
1387 case 0: cmdmod
= ""; break;
1388 case 1: cmdmod
= "-readonly"; break;
1389 case 2: cmdmod
= "-state"; break;
1390 case 3: cmdmod
= "-preserve"; break;
1391 case 4: cmdmod
= "-allbranches"; break;
1393 return std::make_pair(cmdmod
, p
.path
);
1398 struct movie_or_savestate
filetype_movie(false);
1399 struct movie_or_savestate
filetype_savestate(true);
1402 void wxwin_mainwindow::project_selected(const std::string
& id
)
1404 std::string filename
, displayname
;
1405 bool load_ok
= false;
1406 runemufn([id
, &filename
, &displayname
, &load_ok
]() -> void {
1408 auto& p
= project_load(id
); //Check.
1409 filename
= p
.filename
;
1410 displayname
= p
.name
;
1413 switch_projects(id
);
1414 } catch(std::exception
& e
) {
1415 messages
<< "Failed to change project: " << e
.what() << std::endl
;
1419 recentfiles::namedobj obj
;
1421 obj
._filename
= filename
;
1422 obj
._display
= displayname
;
1427 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent
& e
)
1429 std::string filename
;
1430 std::pair
<std::string
, std::string
> filename2
;
1433 case wxID_FRAMEADVANCE
:
1434 platform::queue("+advance-frame");
1435 platform::queue("-advance-frame");
1437 case wxID_SUBFRAMEADVANCE
:
1438 platform::queue("+advance-poll");
1439 platform::queue("-advance-poll");
1442 platform::queue("advance-skiplag");
1445 platform::queue("pause-emulator");
1448 platform::queue("quit-emulator");
1450 case wxID_AUDIO_ENABLED
:
1451 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED
));
1453 case wxID_CANCEL_SAVES
:
1454 platform::queue("cancel-saves");
1456 case wxID_LOAD_MOVIE
:
1457 filename
= choose_file_load(this, "Load Movie", project_moviepath(), filetype_movie
).second
;
1458 recent_movies
->add(filename
);
1459 platform::queue("load-movie " + filename
);
1461 case wxID_LOAD_STATE
:
1462 filename2
= choose_file_load(this, "Load State", project_moviepath(), filetype_savestate
);
1463 recent_movies
->add(filename2
.second
);
1464 platform::queue("load" + filename2
.first
+ " " + filename2
.second
);
1466 case wxID_REWIND_MOVIE
:
1467 platform::queue("rewind-movie");
1469 case wxID_SAVE_MOVIE
:
1470 filename2
= choose_file_save(this, "Save Movie", project_moviepath(), filetype_movie
,
1471 project_prefixname("lsmv"));
1472 recent_movies
->add(filename2
.second
);
1473 platform::queue("save-movie" + filename2
.first
+ " " + filename2
.second
);
1475 case wxID_SAVE_SUBTITLES
:
1476 platform::queue("save-subtitle " + choose_file_save(this, "Save subtitles", project_moviepath(),
1477 filetype_sub
, project_prefixname("sub")));
1479 case wxID_SAVE_STATE
:
1480 filename2
= choose_file_save(this, "Save State", project_moviepath(), filetype_savestate
);
1481 recent_movies
->add(filename2
.second
);
1482 platform::queue("save-state" + filename2
.first
+ " " + filename2
.second
);
1484 case wxID_SAVE_SCREENSHOT
:
1485 platform::queue("take-screenshot " + choose_file_save(this, "Save Screenshot", project_moviepath(),
1486 filetype_png
, get_default_screenshot_name()));
1488 case wxID_RUN_SCRIPT
:
1489 platform::queue("run-script " + pick_file_member(this, "Select Script", project_otherpath()));
1491 case wxID_RUN_LUA
: {
1492 std::string f
= choose_file_load(this, "Select Lua Script", project_otherpath(),
1493 filetype_lua_script
);
1494 platform::queue("run-lua " + f
);
1495 recent_scripts
->add(f
);
1498 case wxID_RESET_LUA
:
1499 platform::queue("reset-lua");
1502 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
1504 case wxID_READONLY_MODE
:
1505 s
= menu_ischecked(wxID_READONLY_MODE
);
1508 lua_callback_movie_lost("readwrite");
1509 if(movb
) movb
.get_movie().readonly_mode(s
);
1510 notify_mode_change(s
);
1512 lua_callback_do_readwrite();
1513 update_movie_state();
1514 graphics_driver_notify_status();
1518 wxeditor_autohold_display(this);
1520 case wxID_EDIT_AUTHORS
:
1521 wxeditor_authors_display(this);
1523 case wxID_EDIT_MACROS
:
1524 wxeditor_macro_display(this);
1526 case wxID_EDIT_SUBTITLES
:
1527 wxeditor_subtitles_display(this);
1529 case wxID_EDIT_VSUBTITLES
:
1530 show_wxeditor_voicesub(this);
1532 case wxID_EDIT_MEMORYWATCH
:
1533 wxeditor_memorywatches_display(this);
1535 case wxID_SAVE_MEMORYWATCH
: {
1536 modal_pause_holder hld
;
1537 std::set
<std::string
> old_watches
;
1538 runemufn([&old_watches
]() { old_watches
= lsnes_memorywatch
.enumerate(); });
1539 std::string filename
= choose_file_save(this, "Save watches to file", project_otherpath(),
1541 std::ofstream
out(filename
.c_str());
1542 for(auto i
: old_watches
) {
1544 runemufn([i
, &val
]() {
1546 val
= lsnes_memorywatch
.get_string(i
);
1547 } catch(std::exception
& e
) {
1548 messages
<< "Can't get value of watch '" << i
<< "': " << e
.what()
1552 out
<< i
<< std::endl
<< val
<< std::endl
;
1557 case wxID_LOAD_MEMORYWATCH
: {
1558 modal_pause_holder hld
;
1559 std::set
<std::string
> old_watches
;
1560 runemufn([&old_watches
]() { old_watches
= lsnes_memorywatch
.enumerate(); });
1561 std::map
<std::string
, std::string
> new_watches
;
1562 std::string filename
= choose_file_load(this, "Choose memory watch file", project_otherpath(),
1565 std::istream
& in
= zip::openrel(filename
, "");
1569 std::getline(in
, wname
);
1570 std::getline(in
, wexpr
);
1571 new_watches
[strip_CR(wname
)] = strip_CR(wexpr
);
1574 } catch(std::exception
& e
) {
1575 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e
.what(),
1576 wxICON_EXCLAMATION
);
1580 runemufn([&new_watches
, &old_watches
]() {
1581 handle_watch_load(new_watches
, old_watches
);
1585 case wxID_MEMORY_SEARCH
:
1586 wxwindow_memorysearch_display();
1589 wxeditor_tasinput_display(this);
1592 std::ostringstream str
;
1593 str
<< "Version: lsnes rr" << lsnes_version
<< std::endl
;
1594 str
<< "Revision: " << lsnes_git_revision
<< std::endl
;
1595 for(auto i
: core_core::all_cores())
1597 str
<< "Core: " << i
->get_core_identifier() << std::endl
;
1598 wxMessageBox(towxstring(str
.str()), _T("About"), wxICON_INFORMATION
| wxOK
, this);
1601 case wxID_SHOW_STATUS
: {
1602 bool newstate
= menu_ischecked(wxID_SHOW_STATUS
);
1605 if(newstate
&& !spanel_shown
)
1606 toplevel
->Add(spanel
, 1, wxGROW
);
1607 else if(!newstate
&& spanel_shown
)
1608 toplevel
->Detach(spanel
);
1611 spanel_shown
= newstate
;
1613 toplevel
->SetSizeHints(this);
1617 case wxID_DEDICATED_MEMORY_WATCH
: {
1618 bool newstate
= menu_ischecked(wxID_DEDICATED_MEMORY_WATCH
);
1619 if(newstate
&& !mwindow
) {
1620 mwindow
= new wxwin_status(-1, "Memory Watch");
1621 spanel
->set_watch_flag(1);
1623 } else if(!newstate
&& mwindow
) {
1626 spanel
->set_watch_flag(0);
1630 case wxID_SET_SPEED
: {
1631 std::string value
= "infinite";
1632 double val
= get_speed_multiplier();
1633 if(!(val
== std::numeric_limits
<double>::infinity()))
1634 value
= (stringfmt() << (100 * val
)).str();
1635 value
= pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value
);
1637 if(value
== "infinite")
1638 set_speed_multiplier(std::numeric_limits
<double>::infinity());
1640 double v
= parse_value
<double>(value
) / 100;
1643 set_speed_multiplier(v
);
1646 wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, this);
1657 set_speed(16.66666666666);
1666 set_speed(33.3333333333333);
1671 case wxID_SPEED_100
:
1674 case wxID_SPEED_150
:
1677 case wxID_SPEED_200
:
1680 case wxID_SPEED_300
:
1683 case wxID_SPEED_500
:
1686 case wxID_SPEED_1000
:
1689 case wxID_SPEED_TURBO
:
1692 case wxID_LOAD_LIBRARY
: {
1693 std::string name
= std::string("load ") + loadlib::library::name();
1694 with_loaded_library(*new loadlib::module(loadlib::library(choose_file_load(this, name
,
1695 project_otherpath(), single_type(loadlib::library::extension(),
1696 loadlib::library::name())))));
1697 handle_post_loadlibrary();
1700 case wxID_PLUGIN_MANAGER
:
1701 wxeditor_plugin_manager_display(this);
1703 case wxID_RELOAD_ROM_IMAGE
:
1705 lsnes_cmd
.invoke("unpause-emulator");
1706 reload_current_rom();
1709 case wxID_NEW_MOVIE
:
1710 show_projectwindow(this);
1712 case wxID_SHOW_MESSAGES
:
1713 msg_window
->reshow();
1715 case wxID_CONFLICTRESOLUTION
:
1716 show_conflictwindow(this);
1718 case wxID_VUDISPLAY
:
1719 open_vumeter_window(this);
1721 case wxID_DISASSEMBLER
:
1722 wxeditor_disassembler_display(this);
1724 case wxID_MOVIE_EDIT
:
1725 wxeditor_movie_display(this);
1727 case wxID_NEW_PROJECT
:
1728 open_new_project_window(this);
1730 case wxID_CLOSE_PROJECT
:
1731 runemufn([]() -> void { project_set(NULL
); });
1733 case wxID_CLOSE_ROM
:
1734 runemufn([]() -> void { close_rom(); });
1736 case wxID_ENTER_FULLSCREEN
:
1737 wx_escape_count
= 0;
1738 enter_or_leave_fullscreen(true);
1740 case wxID_LOAD_ROM_IMAGE_FIRST
:
1741 do_load_rom_image(NULL
);
1743 case wxID_HEXEDITOR
:
1744 wxeditor_hexedit_display(this);
1746 case wxID_MULTITRACK
:
1747 wxeditor_multitrack_display(this);
1750 wxDirDialog
* d
= new wxDirDialog(this, wxT("Change working directory"), wxT("."), wxDD_DIR_MUST_EXIST
);
1751 if(d
->ShowModal() == wxID_CANCEL
) {
1755 std::string path
= tostdstring(d
->GetPath());
1757 chdir(path
.c_str());
1758 messages
<< "Changed working directory to '" << path
<< "'" << std::endl
;
1761 case wxID_DOWNLOAD
: {
1762 if(download_in_progress
) return;
1763 filename
= pick_text(this, "Download movie", "Enter URL to download");
1764 download_in_progress
= new file_download();
1765 download_in_progress
->url
= filename
;
1766 download_in_progress
->target_slot
= "wxwidgets_download_tmp";
1767 download_in_progress
->do_async();
1768 new download_timer(this);
1774 void wxwin_mainwindow::action_updated()
1776 reinterpret_cast<system_menu
*>(sysmenu
)->update(true);
1779 void wxwin_mainwindow::enter_or_leave_fullscreen(bool fs
)
1783 toplevel
->Detach(spanel
);
1786 ShowFullScreen(true);
1788 } else if(!fs
&& is_fs
) {
1789 ShowFullScreen(false);
1792 toplevel
->Add(spanel
, 1, wxGROW
);