Merge status panel and main window
[lsnes.git] / src / plat-wxwidgets / mainwindow.cpp
blob9302b4a8a2660668a8bec9651bb34fdfe3351d3a
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/controllerframe.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/framerate.hpp"
9 #include "core/lua.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/memorywatch.hpp"
12 #include "core/misc.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/window.hpp"
15 #include "core/zip.hpp"
17 #include <vector>
18 #include <string>
20 #include "plat-wxwidgets/menu_dump.hpp"
21 #include "plat-wxwidgets/platform.hpp"
22 #include "plat-wxwidgets/window_mainwindow.hpp"
23 #include "plat-wxwidgets/window_status.hpp"
25 #define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
27 extern "C"
29 #ifndef UINT64_C
30 #define UINT64_C(val) val##ULL
31 #endif
32 #include <libswscale/swscale.h>
35 enum
37 wxID_PAUSE = wxID_HIGHEST + 1,
38 wxID_FRAMEADVANCE,
39 wxID_SUBFRAMEADVANCE,
40 wxID_NEXTPOLL,
41 wxID_ERESET,
42 wxID_AUDIO_ENABLED,
43 wxID_SHOW_AUDIO_STATUS,
44 wxID_AUDIODEV_FIRST,
45 wxID_AUDIODEV_LAST = wxID_AUDIODEV_FIRST + 255,
46 wxID_SAVE_STATE,
47 wxID_SAVE_MOVIE,
48 wxID_LOAD_STATE,
49 wxID_LOAD_STATE_RO,
50 wxID_LOAD_STATE_RW,
51 wxID_LOAD_STATE_P,
52 wxID_LOAD_MOVIE,
53 wxID_RUN_SCRIPT,
54 wxID_RUN_LUA,
55 wxID_EVAL_LUA,
56 wxID_SAVE_SCREENSHOT,
57 wxID_READONLY_MODE,
58 wxID_EDIT_AUTHORS,
59 wxID_AUTOHOLD_FIRST,
60 wxID_AUTOHOLD_LAST = wxID_AUTOHOLD_FIRST + 127,
61 wxID_EDIT_AXES,
62 wxID_EDIT_SETTINGS,
63 wxID_EDIT_KEYBINDINGS,
64 wxID_EDIT_ALIAS,
65 wxID_EDIT_MEMORYWATCH,
66 wxID_SAVE_MEMORYWATCH,
67 wxID_LOAD_MEMORYWATCH,
68 wxID_DUMP_FIRST,
69 wxID_DUMP_LAST = wxID_DUMP_FIRST + 1023,
70 wxID_REWIND_MOVIE,
71 wxID_EDIT_JUKEBOX,
72 wxID_MEMORY_SEARCH
76 namespace
78 unsigned char* screen_buffer;
79 uint32_t old_width;
80 uint32_t old_height;
81 bool main_window_dirty;
82 struct thread* emulation_thread;
84 wxString getname()
86 std::string windowname = "lsnes rr" + lsnes_version + "[" + bsnes_core_version + "]";
87 return towxstring(windowname);
90 struct emu_args
92 struct loaded_rom* rom;
93 struct moviefile* initial;
94 bool load_has_to_succeed;
97 void* emulator_main(void* _args)
99 struct emu_args* args = reinterpret_cast<struct emu_args*>(_args);
100 try {
101 our_rom = args->rom;
102 struct moviefile* movie = args->initial;
103 bool has_to_succeed = args->load_has_to_succeed;
104 platform::flush_command_queue();
105 main_loop(*our_rom, *movie, has_to_succeed);
106 signal_program_exit();
107 } catch(std::bad_alloc& e) {
108 OOM_panic();
109 } catch(std::exception& e) {
110 messages << "FATAL: " << e.what() << std::endl;
111 platform::fatal_error();
113 return NULL;
116 void join_emulator_thread()
118 emulation_thread->join();
121 keygroup mouse_x("mouse_x", keygroup::KT_MOUSE);
122 keygroup mouse_y("mouse_y", keygroup::KT_MOUSE);
123 keygroup mouse_l("mouse_left", keygroup::KT_KEY);
124 keygroup mouse_m("mouse_center", keygroup::KT_KEY);
125 keygroup mouse_r("mouse_right", keygroup::KT_KEY);
126 keygroup mouse_i("mouse_inwindow", keygroup::KT_KEY);
128 void handle_wx_mouse(wxMouseEvent& e)
130 platform::queue(keypress(modifier_set(), mouse_x, e.GetX()));
131 platform::queue(keypress(modifier_set(), mouse_y, e.GetY()));
132 if(e.Entering())
133 platform::queue(keypress(modifier_set(), mouse_i, 1));
134 if(e.Leaving())
135 platform::queue(keypress(modifier_set(), mouse_i, 0));
136 if(e.LeftDown())
137 platform::queue(keypress(modifier_set(), mouse_l, 1));
138 if(e.LeftUp())
139 platform::queue(keypress(modifier_set(), mouse_l, 0));
140 if(e.MiddleDown())
141 platform::queue(keypress(modifier_set(), mouse_m, 1));
142 if(e.MiddleUp())
143 platform::queue(keypress(modifier_set(), mouse_m, 0));
144 if(e.RightDown())
145 platform::queue(keypress(modifier_set(), mouse_r, 1));
146 if(e.RightUp())
147 platform::queue(keypress(modifier_set(), mouse_r, 0));
150 bool is_readonly_mode()
152 bool ret;
153 runemufn([&ret]() { ret = movb.get_movie().readonly_mode(); });
154 return ret;
157 bool UI_get_autohold(unsigned pid, unsigned idx)
159 bool ret;
160 runemufn([&ret, pid, idx]() { ret = controls.autohold(pid, idx); });
161 return ret;
164 void UI_change_autohold(unsigned pid, unsigned idx, bool newstate)
166 runemufn([pid, idx, newstate]() { controls.autohold(pid, idx, newstate); });
169 int UI_controller_index_by_logical(unsigned lid)
171 int ret;
172 runemufn([&ret, lid]() { ret = controls.lcid_to_pcid(lid); });
173 return ret;
176 int UI_button_id(unsigned pcid, unsigned lidx)
178 int ret;
179 runemufn([&ret, pcid, lidx]() { ret = controls.button_id(pcid, lidx); });
180 return ret;
183 class controller_autohold_menu : public wxMenu
185 public:
186 controller_autohold_menu(unsigned lid, enum devicetype_t dtype);
187 void change_type();
188 bool is_dummy();
189 void on_select(wxCommandEvent& e);
190 void update(unsigned pid, unsigned ctrlnum, bool newstate);
191 private:
192 unsigned our_lid;
193 wxMenuItem* entries[MAX_LOGICAL_BUTTONS];
194 unsigned enabled_entries;
197 class autohold_menu : public wxMenu
199 public:
200 autohold_menu(wxwin_mainwindow* win);
201 void reconfigure();
202 void on_select(wxCommandEvent& e);
203 void update(unsigned pid, unsigned ctrlnum, bool newstate);
204 private:
205 controller_autohold_menu* menus[MAXCONTROLLERS];
206 wxMenuItem* entries[MAXCONTROLLERS];
209 class sound_select_menu : public wxMenu
211 public:
212 sound_select_menu(wxwin_mainwindow* win);
213 void update(const std::string& dev);
214 void on_select(wxCommandEvent& e);
215 private:
216 std::map<std::string, wxMenuItem*> items;
217 std::map<int, std::string> devices;
220 class sound_select_menu;
222 class broadcast_listener : public information_dispatch
224 public:
225 broadcast_listener(wxwin_mainwindow* win);
226 void set_sound_select(sound_select_menu* sdev);
227 void set_autohold_menu(autohold_menu* ah);
228 void on_sound_unmute(bool unmute) throw();
229 void on_sound_change(const std::string& dev) throw();
230 void on_mode_change(bool readonly) throw();
231 void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
232 void on_autohold_reconfigure();
233 private:
234 wxwin_mainwindow* mainw;
235 sound_select_menu* sounddev;
236 autohold_menu* ahmenu;
239 controller_autohold_menu::controller_autohold_menu(unsigned lid, enum devicetype_t dtype)
241 platform::set_modal_pause(true);
242 our_lid = lid;
243 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
244 int id = wxID_AUTOHOLD_FIRST + MAX_LOGICAL_BUTTONS * lid + i;
245 entries[i] = AppendCheckItem(id, towxstring(get_logical_button_name(i)));
247 change_type();
248 platform::set_modal_pause(false);
251 void controller_autohold_menu::change_type()
253 enabled_entries = 0;
254 int pid = controls.lcid_to_pcid(our_lid);
255 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
256 int pidx = -1;
257 if(pid >= 0)
258 pidx = controls.button_id(pid, i);
259 if(pidx >= 0) {
260 entries[i]->Check(pid > 0 && UI_get_autohold(pid, pidx));
261 entries[i]->Enable();
262 enabled_entries++;
263 } else {
264 entries[i]->Check(false);
265 entries[i]->Enable(false);
270 bool controller_autohold_menu::is_dummy()
272 return !enabled_entries;
275 void controller_autohold_menu::on_select(wxCommandEvent& e)
277 int x = e.GetId();
278 if(x < wxID_AUTOHOLD_FIRST + our_lid * MAX_LOGICAL_BUTTONS || x >= wxID_AUTOHOLD_FIRST *
279 (our_lid + 1) * MAX_LOGICAL_BUTTONS) {
280 return;
282 unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % MAX_LOGICAL_BUTTONS;
283 platform::set_modal_pause(true);
284 int pid = controls.lcid_to_pcid(our_lid);
285 if(pid < 0 || !entries[lidx]) {
286 platform::set_modal_pause(false);
287 return;
289 int pidx = controls.button_id(pid, lidx);
290 if(pidx < 0) {
291 platform::set_modal_pause(false);
292 return;
294 //Autohold change on pid=pid, ctrlindx=idx, state
295 bool newstate = entries[lidx]->IsChecked();
296 UI_change_autohold(pid, pidx, newstate);
297 platform::set_modal_pause(false);
300 void controller_autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
302 platform::set_modal_pause(true);
303 int pid2 = UI_controller_index_by_logical(our_lid);
304 if(pid2 < 0 || static_cast<unsigned>(pid) != pid2) {
305 platform::set_modal_pause(false);
306 return;
308 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
309 int idx = UI_button_id(pid2, i);
310 if(idx < 0 || static_cast<unsigned>(idx) != ctrlnum)
311 continue;
312 entries[i]->Check(newstate);
314 platform::set_modal_pause(false);
318 autohold_menu::autohold_menu(wxwin_mainwindow* win)
320 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
321 std::ostringstream str;
322 str << "Controller #&" << (i + 1);
323 menus[i] = new controller_autohold_menu(i, DT_NONE);
324 entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
325 entries[i]->Enable(!menus[i]->is_dummy());
327 win->Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED,
328 wxCommandEventHandler(autohold_menu::on_select), NULL, this);
329 reconfigure();
332 void autohold_menu::reconfigure()
334 platform::set_modal_pause(true);
335 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
336 menus[i]->change_type();
337 entries[i]->Enable(!menus[i]->is_dummy());
339 platform::set_modal_pause(false);
342 void autohold_menu::on_select(wxCommandEvent& e)
344 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
345 menus[i]->on_select(e);
348 void autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
350 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
351 menus[i]->update(pid, ctrlnum, newstate);
354 sound_select_menu::sound_select_menu(wxwin_mainwindow* win)
356 std::string curdev = platform::get_sound_device();
357 int j = wxID_AUDIODEV_FIRST;
358 for(auto i : platform::get_sound_devices()) {
359 items[i.first] = AppendRadioItem(j, towxstring(i.first + "(" + i.second + ")"));
360 devices[j] = i.first;
361 if(i.first == curdev)
362 items[i.first]->Check();
363 win->Connect(j, wxEVT_COMMAND_MENU_SELECTED,
364 wxCommandEventHandler(sound_select_menu::on_select), NULL, this);
365 j++;
369 void sound_select_menu::update(const std::string& dev)
371 items[dev]->Check();
374 void _do_sound_select(void* args)
376 std::string* x = reinterpret_cast<std::string*>(args);
377 platform::set_sound_device(*x);
380 void sound_select_menu::on_select(wxCommandEvent& e)
382 std::string devname = devices[e.GetId()];
383 if(devname != "")
384 runemufn([devname]() { platform::set_sound_device(devname); });
387 broadcast_listener::broadcast_listener(wxwin_mainwindow* win)
388 : information_dispatch("wxwidgets-broadcast-listener")
390 mainw = win;
393 void broadcast_listener::set_sound_select(sound_select_menu* sdev)
395 sounddev = sdev;
398 void broadcast_listener::set_autohold_menu(autohold_menu* ah)
400 ahmenu = ah;
403 void broadcast_listener::on_sound_unmute(bool unmute) throw()
405 runuifun([unmute, mainw]() { mainw->menu_check(wxID_AUDIO_ENABLED, unmute); });
408 void broadcast_listener::on_sound_change(const std::string& dev) throw()
410 runuifun([dev, sounddev]() { if(sounddev) sounddev->update(dev); });
413 void broadcast_listener::on_mode_change(bool readonly) throw()
415 runuifun([readonly, mainw]() { mainw->menu_check(wxID_READONLY_MODE, readonly); });
418 void broadcast_listener::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
420 runuifun([pid, ctrlnum, newstate, ahmenu]() { ahmenu->update(pid, ctrlnum, newstate); });
423 void broadcast_listener::on_autohold_reconfigure()
425 runuifun([ahmenu]() { ahmenu->reconfigure(); });
428 void _set_readonly(void* args)
430 bool s = *reinterpret_cast<bool*>(args);
431 movb.get_movie().readonly_mode(s);
432 if(!s)
433 lua_callback_do_readwrite();
434 update_movie_state();
437 struct keyentry_mod_data
439 wxCheckBox* pressed;
440 wxCheckBox* unmasked;
441 unsigned tmpflags;
444 class wxdialog_keyentry : public wxDialog
446 public:
447 wxdialog_keyentry(wxWindow* parent);
448 void on_change_setting(wxCommandEvent& e);
449 void on_ok(wxCommandEvent& e);
450 void on_cancel(wxCommandEvent& e);
451 std::string getkey();
452 private:
453 std::map<std::string, keyentry_mod_data> modifiers;
454 wxComboBox* mainkey;
455 wxButton* ok;
456 wxButton* cancel;
459 wxdialog_keyentry::wxdialog_keyentry(wxWindow* parent)
460 : wxDialog(parent, wxID_ANY, wxT("Specify key"), wxDefaultPosition, wxSize(-1, -1))
462 std::vector<wxString> keych;
463 std::set<std::string> mods, keys;
465 runemufn([&mods, &keys]() { mods = modifier::get_set(); keys = keygroup::get_keys(); });
466 Centre();
467 wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
468 SetSizer(top_s);
470 wxFlexGridSizer* t_s = new wxFlexGridSizer(mods.size() + 1, 3, 0, 0);
471 for(auto i : mods) {
472 t_s->Add(new wxStaticText(this, wxID_ANY, towxstring(i)), 0, wxGROW);
473 keyentry_mod_data m;
474 t_s->Add(m.pressed = new wxCheckBox(this, wxID_ANY, wxT("Pressed")), 0, wxGROW);
475 t_s->Add(m.unmasked = new wxCheckBox(this, wxID_ANY, wxT("Unmasked")), 0, wxGROW);
476 m.pressed->Disable();
477 modifiers[i] = m;
478 m.pressed->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
479 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
480 m.unmasked->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
481 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
483 for(auto i : keys)
484 keych.push_back(towxstring(i));
485 t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Key")), 0, wxGROW);
486 t_s->Add(mainkey = new wxComboBox(this, wxID_ANY, keych[0], wxDefaultPosition, wxDefaultSize,
487 keych.size(), &keych[0], wxCB_READONLY), 1, wxGROW);
488 mainkey->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
489 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
490 top_s->Add(t_s);
492 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
493 pbutton_s->AddStretchSpacer();
494 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
495 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
496 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
497 wxCommandEventHandler(wxdialog_keyentry::on_ok), NULL, this);
498 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
499 wxCommandEventHandler(wxdialog_keyentry::on_cancel), NULL, this);
500 top_s->Add(pbutton_s, 0, wxGROW);
502 t_s->SetSizeHints(this);
503 top_s->SetSizeHints(this);
504 Fit();
507 #define TMPFLAG_UNMASKED 65
508 #define TMPFLAG_UNMASKED_LINK_CHILD 2
509 #define TMPFLAG_UNMASKED_LINK_PARENT 68
510 #define TMPFLAG_PRESSED 8
511 #define TMPFLAG_PRESSED_LINK_CHILD 16
512 #define TMPFLAG_PRESSED_LINK_PARENT 32
514 void wxdialog_keyentry::on_change_setting(wxCommandEvent& e)
516 for(auto& i : modifiers)
517 i.second.tmpflags = 0;
518 for(auto& i : modifiers) {
519 modifier* m = NULL;
520 try {
521 m = &modifier::lookup(i.first);
522 } catch(...) {
523 i.second.pressed->Disable();
524 i.second.unmasked->Disable();
525 continue;
527 std::string j = m->linked_name();
528 if(i.second.unmasked->GetValue())
529 i.second.tmpflags |= TMPFLAG_UNMASKED;
530 if(j != "") {
531 if(modifiers[j].unmasked->GetValue())
532 i.second.tmpflags |= TMPFLAG_UNMASKED_LINK_PARENT;
533 if(i.second.unmasked->GetValue())
534 modifiers[j].tmpflags |= TMPFLAG_UNMASKED_LINK_CHILD;
536 if(i.second.pressed->GetValue())
537 i.second.tmpflags |= TMPFLAG_PRESSED;
538 if(j != "") {
539 if(modifiers[j].pressed->GetValue())
540 i.second.tmpflags |= TMPFLAG_PRESSED_LINK_PARENT;
541 if(i.second.pressed->GetValue())
542 modifiers[j].tmpflags |= TMPFLAG_PRESSED_LINK_CHILD;
545 for(auto& i : modifiers) {
546 //Unmasked is to be enabled if neither unmasked link flag is set.
547 if(i.second.tmpflags & ((TMPFLAG_UNMASKED_LINK_CHILD | TMPFLAG_UNMASKED_LINK_PARENT) & ~64)) {
548 i.second.unmasked->SetValue(false);
549 i.second.unmasked->Disable();
550 } else
551 i.second.unmasked->Enable();
552 //Pressed is to be enabled if:
553 //- This modifier is unmasked or parent is unmasked.
554 //- Parent nor child is not pressed.
555 if(((i.second.tmpflags & (TMPFLAG_UNMASKED | TMPFLAG_UNMASKED_LINK_PARENT |
556 TMPFLAG_PRESSED_LINK_CHILD | TMPFLAG_PRESSED_LINK_PARENT)) & 112) == 64)
557 i.second.pressed->Enable();
558 else {
559 i.second.pressed->SetValue(false);
560 i.second.pressed->Disable();
565 void wxdialog_keyentry::on_ok(wxCommandEvent& e)
567 EndModal(wxID_OK);
570 void wxdialog_keyentry::on_cancel(wxCommandEvent& e)
572 EndModal(wxID_CANCEL);
575 std::string wxdialog_keyentry::getkey()
577 std::string x;
578 bool f;
579 f = true;
580 for(auto i : modifiers) {
581 if(i.second.pressed->GetValue()) {
582 if(!f)
583 x = x + ",";
584 f = false;
585 x = x + i.first;
588 x = x + "/";
589 f = true;
590 for(auto i : modifiers) {
591 if(i.second.unmasked->GetValue()) {
592 if(!f)
593 x = x + ",";
594 f = false;
595 x = x + i.first;
598 x = x + "|" + tostdstring(mainkey->GetValue());
599 return x;
603 void boot_emulator(loaded_rom& rom, moviefile& movie)
605 try {
606 struct emu_args* a = new emu_args;
607 a->rom = &rom;
608 a->initial = &movie;
609 a->load_has_to_succeed = false;
610 platform::set_modal_pause(true);
611 emulation_thread = &thread::create(emulator_main, a);
612 main_window = new wxwin_mainwindow();
613 main_window->Show();
614 platform::set_modal_pause(false);
615 } catch(std::bad_alloc& e) {
616 OOM_panic();
620 wxwin_mainwindow::panel::panel(wxWindow* win)
621 : wxPanel(win)
623 this->Connect(wxEVT_PAINT, wxPaintEventHandler(panel::on_paint), NULL, this);
624 this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(panel::on_erase), NULL, this);
625 this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(panel::on_keyboard_down), NULL, this);
626 this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(panel::on_keyboard_up), NULL, this);
627 this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
628 this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
629 this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
630 this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
631 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
632 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
633 this->Connect(wxEVT_MOTION, wxMouseEventHandler(panel::on_mouse), NULL, this);
634 this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(panel::on_mouse), NULL, this);
635 this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(panel::on_mouse), NULL, this);
636 SetMinSize(wxSize(512, 448));
639 void wxwin_mainwindow::menu_start(wxString name)
641 while(!upper.empty())
642 upper.pop();
643 current_menu = new wxMenu();
644 menubar->Append(current_menu, name);
647 void wxwin_mainwindow::menu_special(wxString name, wxMenu* menu)
649 while(!upper.empty())
650 upper.pop();
651 menubar->Append(menu, name);
652 current_menu = NULL;
655 void wxwin_mainwindow::menu_special_sub(wxString name, wxMenu* menu)
657 current_menu->AppendSubMenu(menu, name);
660 void wxwin_mainwindow::menu_entry(int id, wxString name)
662 current_menu->Append(id, name);
663 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
664 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
667 void wxwin_mainwindow::menu_entry_check(int id, wxString name)
669 checkitems[id] = current_menu->AppendCheckItem(id, name);
670 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
671 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
674 void wxwin_mainwindow::menu_start_sub(wxString name)
676 wxMenu* old = current_menu;
677 upper.push(current_menu);
678 current_menu = new wxMenu();
679 old->AppendSubMenu(current_menu, name);
682 void wxwin_mainwindow::menu_end_sub(wxString name)
684 current_menu = upper.top();
685 upper.pop();
688 bool wxwin_mainwindow::menu_ischecked(int id)
690 if(checkitems.count(id))
691 return checkitems[id]->IsChecked();
692 else
693 return false;
696 void wxwin_mainwindow::menu_check(int id, bool newstate)
698 if(checkitems.count(id))
699 return checkitems[id]->Check(newstate);
700 else
701 return;
704 void wxwin_mainwindow::menu_separator()
706 current_menu->AppendSeparator();
709 void wxwin_mainwindow::panel::request_paint()
711 Refresh();
714 void wxwin_mainwindow::panel::on_paint(wxPaintEvent& e)
716 render_framebuffer();
717 static struct SwsContext* ctx;
718 uint8_t* srcp[1];
719 int srcs[1];
720 uint8_t* dstp[1];
721 int dsts[1];
722 wxPaintDC dc(this);
723 if(!screen_buffer || main_screen.width != old_width || main_screen.height != old_height) {
724 if(screen_buffer)
725 delete[] screen_buffer;
726 screen_buffer = new unsigned char[main_screen.width * main_screen.height * 3];
727 old_height = main_screen.height;
728 old_width = main_screen.width;
729 uint32_t w = main_screen.width;
730 uint32_t h = main_screen.height;
731 if(w && h)
732 ctx = sws_getCachedContext(ctx, w, h, PIX_FMT_RGBA, w, h, PIX_FMT_BGR24, SWS_POINT |
733 SWS_CPU_CAPS_MMX2, NULL, NULL, NULL);
734 if(w < 512)
735 w = 512;
736 if(h < 448)
737 h = 448;
738 SetMinSize(wxSize(w, h));
739 main_window->Fit();
741 srcs[0] = 4 * main_screen.width;
742 dsts[0] = 3 * main_screen.width;
743 srcp[0] = reinterpret_cast<unsigned char*>(main_screen.memory);
744 dstp[0] = screen_buffer;
745 memset(screen_buffer, 0, main_screen.width * main_screen.height * 3);
746 uint64_t t1 = get_utime();
747 if(main_screen.width && main_screen.height)
748 sws_scale(ctx, srcp, srcs, 0, main_screen.height, dstp, dsts);
749 uint64_t t2 = get_utime();
750 wxBitmap bmp(wxImage(main_screen.width, main_screen.height, screen_buffer, true));
751 uint64_t t3 = get_utime();
752 dc.DrawBitmap(bmp, 0, 0, false);
753 main_window_dirty = false;
756 void wxwin_mainwindow::panel::on_erase(wxEraseEvent& e)
758 //Blank.
761 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent& e)
763 handle_wx_keyboard(e, true);
766 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent& e)
768 handle_wx_keyboard(e, false);
771 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent& e)
773 handle_wx_mouse(e);
776 wxwin_mainwindow::wxwin_mainwindow()
777 : wxFrame(NULL, wxID_ANY, getname(), wxDefaultPosition, wxSize(-1, -1),
778 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
780 broadcast_listener* blistener = new broadcast_listener(this);
781 Centre();
782 wxFlexGridSizer* toplevel = new wxFlexGridSizer(1, 2, 0, 0);
783 toplevel->Add(gpanel = new panel(this), 1, wxGROW);
784 toplevel->Add(spanel = new wxwin_status::panel(this, 20), 1, wxGROW);
785 toplevel->SetSizeHints(this);
786 SetSizer(toplevel);
787 Fit();
788 gpanel->SetFocus();
789 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_mainwindow::on_close));
790 menubar = new wxMenuBar;
791 SetMenuBar(menubar);
793 //TOP-level accels: ACFOS.
794 //System menu: (ACFOS)EMNPQRU
795 menu_start(wxT("&System"));
796 menu_entry(wxID_FRAMEADVANCE, wxT("Fra&me advance"));
797 menu_entry(wxID_SUBFRAMEADVANCE, wxT("S&ubframe advance"));
798 menu_entry(wxID_NEXTPOLL, wxT("&Next poll"));
799 menu_entry(wxID_PAUSE, wxT("&Pause/Unpause"));
800 menu_separator();
801 menu_entry(wxID_ERESET, wxT("&Reset"));
802 menu_separator();
803 menu_entry(wxID_EDIT_AUTHORS, wxT("&Edit game name && authors"));
804 menu_separator();
805 menu_entry(wxID_EXIT, wxT("&Quit"));
806 menu_separator();
807 menu_entry(wxID_ABOUT, wxT("About"));
808 //File menu: (ACFOS)DELMNPRTUVW
809 menu_start(wxT("&File"));
810 menu_entry_check(wxID_READONLY_MODE, wxT("Reado&nly mode"));
811 menu_check(wxID_READONLY_MODE, is_readonly_mode());
812 menu_separator();
813 menu_entry(wxID_SAVE_STATE, wxT("Save stat&e"));
814 menu_entry(wxID_SAVE_MOVIE, wxT("Sa&ve movie"));
815 menu_separator();
816 menu_entry(wxID_LOAD_STATE, wxT("&Load state"));
817 menu_entry(wxID_LOAD_STATE_RO, wxT("Loa&d state (readonly)"));
818 menu_entry(wxID_LOAD_STATE_RW, wxT("Load s&tate (read-write)"));
819 menu_entry(wxID_LOAD_STATE_P, wxT("Load state (&preserve)"));
820 menu_entry(wxID_LOAD_MOVIE, wxT("Load &movie"));
821 menu_entry(wxID_REWIND_MOVIE, wxT("Re&wind movie"));
822 menu_separator();
823 menu_entry(wxID_SAVE_SCREENSHOT, wxT("Save sc&reenshot"));
824 menu_separator();
825 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
826 wxID_DUMP_FIRST, wxID_DUMP_LAST)));
827 //Autohold menu: (ACFOS)
828 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu*>(ahmenu = new autohold_menu(this)));
829 blistener->set_autohold_menu(reinterpret_cast<autohold_menu*>(ahmenu));
830 //Scripting menu: (ACFOS)ERU
831 menu_start(wxT("S&cripting"));
832 menu_entry(wxID_RUN_SCRIPT, wxT("&Run script"));
833 if(lua_supported) {
834 menu_separator();
835 menu_entry(wxID_EVAL_LUA, wxT("&Evaluate Lua statement"));
836 menu_entry(wxID_RUN_LUA, wxT("R&un Lua script"));
838 menu_separator();
839 menu_entry(wxID_EDIT_MEMORYWATCH, wxT("Edit memory watch"));
840 menu_separator();
841 menu_entry(wxID_LOAD_MEMORYWATCH, wxT("Load memory watch"));
842 menu_entry(wxID_SAVE_MEMORYWATCH, wxT("Save memory watch"));
843 menu_separator();
844 menu_entry(wxID_MEMORY_SEARCH, wxT("Memory Search"));
845 //Settings menu: (ACFOS)
846 menu_start(wxT("Settings"));
847 menu_entry(wxID_EDIT_AXES, wxT("Configure axes"));
848 menu_entry(wxID_EDIT_SETTINGS, wxT("Configure settings"));
849 menu_entry(wxID_EDIT_KEYBINDINGS, wxT("Configure keybindings"));
850 menu_entry(wxID_EDIT_ALIAS, wxT("Configure aliases"));
851 menu_entry(wxID_EDIT_JUKEBOX, wxT("Configure jukebox"));
852 if(platform::sound_initialized()) {
853 //Sound menu: (ACFOS)EHU
854 menu_start(wxT("S&ound"));
855 menu_entry_check(wxID_AUDIO_ENABLED, wxT("So&unds enabled"));
856 menu_check(wxID_AUDIO_ENABLED, platform::is_sound_enabled());
857 menu_entry(wxID_SHOW_AUDIO_STATUS, wxT("S&how audio status"));
858 menu_separator();
859 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu*>(sounddev =
860 new sound_select_menu(this)));
861 blistener->set_sound_select(reinterpret_cast<sound_select_menu*>(sounddev));
865 void wxwin_mainwindow::request_paint()
867 gpanel->Refresh();
870 void wxwin_mainwindow::on_close(wxCloseEvent& e)
872 //Veto it for now, latter things will delete it.
873 e.Veto();
874 platform::queue("quit-emulator");
877 void wxwin_mainwindow::notify_update() throw()
879 if(!main_window_dirty) {
880 main_window_dirty = true;
881 gpanel->Refresh();
885 void wxwin_mainwindow::notify_update_status() throw()
887 if(!spanel->dirty) {
888 spanel->dirty = true;
889 spanel->Refresh();
893 void wxwin_mainwindow::notify_exit() throw()
895 join_emulator_thread();
896 Destroy();
899 void wxwin_mainwindow::handle_menu_click(wxCommandEvent& e)
901 wxFileDialog* d;
902 wxTextEntryDialog* d2;
903 std::string filename;
904 bool s;
905 switch(e.GetId()) {
906 case wxID_FRAMEADVANCE:
907 platform::queue("+advance-frame");
908 platform::queue("-advance-frame");
909 return;
910 case wxID_SUBFRAMEADVANCE:
911 platform::queue("+advance-poll");
912 platform::queue("-advance-poll");
913 return;
914 case wxID_NEXTPOLL:
915 platform::queue("advance-skiplag");
916 return;
917 case wxID_PAUSE:
918 platform::queue("pause-emulator");
919 return;
920 case wxID_RESET:
921 platform::queue("reset");
922 return;
923 case wxID_EXIT:
924 platform::queue("quit-emulator");
925 return;
926 case wxID_AUDIO_ENABLED:
927 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED));
928 return;
929 case wxID_SHOW_AUDIO_STATUS:
930 platform::queue("show-sound-status");
931 return;
932 case wxID_LOAD_MOVIE:
933 d = new wxFileDialog(this, wxT("Load Movie"), wxT("."));
934 if(d->ShowModal() == wxID_OK)
935 filename = tostdstring(d->GetPath());
936 d->Destroy();
937 if(filename == "")
938 break;
939 platform::queue("load-movie " + filename);
940 break;
941 case wxID_LOAD_STATE:
942 d = new wxFileDialog(this, wxT("Load State"), wxT("."));
943 if(d->ShowModal() == wxID_OK)
944 filename = tostdstring(d->GetPath());
945 d->Destroy();
946 if(filename == "")
947 break;
948 platform::queue("load " + filename);
949 break;
950 case wxID_LOAD_STATE_RO:
951 d = new wxFileDialog(this, wxT("Load State (Read-Only)"), wxT("."));
952 if(d->ShowModal() == wxID_OK)
953 filename = tostdstring(d->GetPath());
954 d->Destroy();
955 if(filename == "")
956 break;
957 platform::queue("load-readonly " + filename);
958 break;
959 case wxID_LOAD_STATE_RW:
960 d = new wxFileDialog(this, wxT("Load State (Read-Write)"), wxT("."));
961 if(d->ShowModal() == wxID_OK)
962 filename = tostdstring(d->GetPath());
963 d->Destroy();
964 if(filename == "")
965 break;
966 platform::queue("load-state " + filename);
967 break;
968 case wxID_LOAD_STATE_P:
969 d = new wxFileDialog(this, wxT("Load State (Preserve)"), wxT("."));
970 if(d->ShowModal() == wxID_OK)
971 filename = tostdstring(d->GetPath());
972 d->Destroy();
973 if(filename == "")
974 break;
975 platform::queue("load-preserve " + filename);
976 break;
977 case wxID_REWIND_MOVIE:
978 platform::queue("rewind-movie");
979 break;
980 case wxID_SAVE_MOVIE:
981 d = new wxFileDialog(this, wxT("Save Movie"), wxT("."));
982 if(d->ShowModal() == wxID_OK)
983 filename = tostdstring(d->GetPath());
984 d->Destroy();
985 if(filename == "")
986 break;
987 platform::queue("save-movie " + filename);
988 break;
989 case wxID_SAVE_STATE:
990 d = new wxFileDialog(this, wxT("Save State"), wxT("."));
991 if(d->ShowModal() == wxID_OK)
992 filename = tostdstring(d->GetPath());
993 d->Destroy();
994 if(filename == "")
995 break;
996 platform::queue("save-state " + filename);
997 break;
998 case wxID_SAVE_SCREENSHOT:
999 d = new wxFileDialog(this, wxT("Save Screenshot"), wxT("."));
1000 if(d->ShowModal() == wxID_OK)
1001 filename = tostdstring(d->GetPath());
1002 d->Destroy();
1003 if(filename == "")
1004 break;
1005 platform::queue("take-screenshot " + filename);
1006 break;
1007 case wxID_RUN_SCRIPT:
1008 d = new wxFileDialog(this, wxT("Select Script"), wxT("."));
1009 if(d->ShowModal() == wxID_OK)
1010 filename = tostdstring(d->GetPath());
1011 d->Destroy();
1012 if(filename == "")
1013 break;
1014 platform::queue("run-script " + filename);
1015 break;
1016 case wxID_RUN_LUA:
1017 d = new wxFileDialog(this, wxT("Select Lua Script"), wxT("."));
1018 if(d->ShowModal() == wxID_OK)
1019 filename = tostdstring(d->GetPath());
1020 d->Destroy();
1021 if(filename == "")
1022 break;
1023 platform::queue("run-lua " + filename);
1024 break;
1025 case wxID_EVAL_LUA:
1026 d2 = new wxTextEntryDialog(this, wxT("Enter Lua statement:"), wxT("Evaluate Lua"));
1027 if(d2->ShowModal() == wxID_OK)
1028 filename = tostdstring(d2->GetValue());
1029 d2->Destroy();
1030 platform::queue("evaluate-lua " + filename);
1031 break;
1032 case wxID_READONLY_MODE:
1033 s = menu_ischecked(wxID_READONLY_MODE);
1034 platform::queue(_set_readonly, &s, true);
1035 break;
1036 case wxID_EDIT_AXES:
1037 wxeditor_axes_display(this);
1038 break;
1039 case wxID_EDIT_AUTHORS:
1040 wxeditor_authors_display(this);
1041 break;
1042 case wxID_EDIT_SETTINGS:
1043 wxeditor_settings_display(this);
1044 break;
1045 case wxID_EDIT_KEYBINDINGS:
1046 menu_edit_keybindings(e);
1047 break;
1048 case wxID_EDIT_ALIAS:
1049 menu_edit_aliases(e);
1050 break;
1051 case wxID_EDIT_JUKEBOX:
1052 menu_edit_jukebox(e);
1053 break;
1054 case wxID_EDIT_MEMORYWATCH:
1055 menu_edit_memorywatch(e);
1056 break;
1057 case wxID_SAVE_MEMORYWATCH:
1058 menu_save_memorywatch(e);
1059 break;
1060 case wxID_LOAD_MEMORYWATCH:
1061 menu_load_memorywatch(e);
1062 break;
1063 case wxID_MEMORY_SEARCH:
1064 wxwindow_memorysearch_display();
1065 break;
1066 case wxID_ABOUT: {
1067 std::ostringstream str;
1068 str << "Version: lsnes rr" << lsnes_version << std::endl;
1069 str << "Revision: " << lsnes_git_revision << std::endl;
1070 str << "Core: " << bsnes_core_version << std::endl;
1071 wxMessageBox(towxstring(str.str()), _T("About"), wxICON_INFORMATION | wxOK, this);
1073 break;
1077 #define NEW_KEYBINDING "A new binding..."
1078 #define NEW_ALIAS "A new alias..."
1079 #define NEW_WATCH "A new watch..."
1081 void wxwin_mainwindow::menu_edit_keybindings(wxCommandEvent& e)
1083 platform::set_modal_pause(true);
1084 std::set<std::string> bind;
1085 runemufn([&bind]() { bind = keymapper::get_bindings(); });
1086 std::vector<wxString> choices;
1087 choices.push_back(wxT(NEW_KEYBINDING));
1088 for(auto i : bind)
1089 choices.push_back(towxstring(i));
1090 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1091 wxT("Select binding"), choices.size(), &choices[0]);
1092 if(d->ShowModal() == wxID_CANCEL) {
1093 d->Destroy();
1094 platform::set_modal_pause(false);
1095 return;
1097 std::string key = tostdstring(d->GetStringSelection());
1098 d->Destroy();
1099 if(key == NEW_KEYBINDING) {
1100 wxdialog_keyentry* d2 = new wxdialog_keyentry(this);
1101 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1102 // wxT("Edit binding"), wxT(""));
1103 if(d2->ShowModal() == wxID_CANCEL) {
1104 d2->Destroy();
1105 platform::set_modal_pause(false);
1106 return;
1108 key = d2->getkey();
1109 //key = tostdstring(d2->GetValue());
1110 d2->Destroy();
1112 std::string old_command_value;
1113 runemufn([&old_command_value, key]() { old_command_value = keymapper::get_command_for(key); });
1114 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1115 towxstring(old_command_value));
1116 if(d4->ShowModal() == wxID_CANCEL) {
1117 d4->Destroy();
1118 platform::set_modal_pause(false);
1119 return;
1121 bool fault = false;
1122 std::string faulttext;
1123 std::string newcommand = tostdstring(d4->GetValue());
1124 runemufn([&fault, &faulttext, key, newcommand]() {
1125 try {
1126 keymapper::bind_for(key, newcommand);
1127 } catch(std::exception& e) {
1130 if(fault) {
1131 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1132 faulttext), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1133 d3->ShowModal();
1134 d3->Destroy();
1136 d4->Destroy();
1137 platform::set_modal_pause(false);
1140 void strip_CR(std::string& x) throw(std::bad_alloc);
1142 void wxwin_mainwindow::menu_edit_aliases(wxCommandEvent& e)
1144 platform::set_modal_pause(true);
1145 std::set<std::string> bind;
1146 runemufn([&bind]() { bind = command::get_aliases(); });
1147 std::vector<wxString> choices;
1148 choices.push_back(wxT(NEW_ALIAS));
1149 for(auto i : bind)
1150 choices.push_back(towxstring(i));
1151 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1152 wxT("Select alias"), choices.size(), &choices[0]);
1153 if(d->ShowModal() == wxID_CANCEL) {
1154 d->Destroy();
1155 platform::set_modal_pause(false);
1156 return;
1158 std::string alias = tostdstring(d->GetStringSelection());
1159 d->Destroy();
1160 if(alias == NEW_ALIAS) {
1161 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1162 wxT("Enter alias name"));
1163 if(d2->ShowModal() == wxID_CANCEL) {
1164 d2->Destroy();
1165 platform::set_modal_pause(false);
1166 return;
1168 alias = tostdstring(d2->GetValue());
1169 d2->Destroy();
1170 if(!command::valid_alias_name(alias)) {
1171 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1172 "name: ") + alias), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1173 d3->ShowModal();
1174 d3->Destroy();
1175 platform::set_modal_pause(false);
1176 return;
1179 std::string old_alias_value = command::get_alias_for(alias);
1180 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1181 towxstring(old_alias_value), wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE);
1182 if(d4->ShowModal() == wxID_CANCEL) {
1183 d4->Destroy();
1184 platform::set_modal_pause(false);
1185 return;
1187 std::string newcmd = tostdstring(d4->GetValue());
1188 runemufn([alias, newcmd]() { command::set_alias_for(alias, newcmd); });
1189 d4->Destroy();
1190 platform::set_modal_pause(false);
1193 void wxwin_mainwindow::menu_edit_jukebox(wxCommandEvent& e)
1195 platform::set_modal_pause(true);
1196 std::string x;
1197 std::vector<std::string> new_jukebox;
1198 runemufn([&x]() {
1199 for(auto i : get_jukebox_names())
1200 x = x + i + "\n";
1203 wxTextEntryDialog* dialog = new wxTextEntryDialog(this, wxT("List jukebox entries"), wxT("Configure jukebox"),
1204 towxstring(x), wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE);
1205 if(dialog->ShowModal() == wxID_CANCEL) {
1206 dialog->Destroy();
1207 platform::set_modal_pause(false);
1208 return;
1210 x = tostdstring(dialog->GetValue());
1211 dialog->Destroy();
1213 while(x != "") {
1214 size_t split = x.find_first_of("\n");
1215 std::string l;
1216 if(split < x.length()) {
1217 l = x.substr(0, split);
1218 x = x.substr(split + 1);
1219 } else {
1220 l = x;
1221 x = "";
1223 strip_CR(l);
1224 if(l != "")
1225 new_jukebox.push_back(l);
1227 runemufn([&new_jukebox]() { set_jukebox_names(new_jukebox); });
1228 notify_update_status();
1229 platform::set_modal_pause(false);
1232 void wxwin_mainwindow::menu_load_memorywatch(wxCommandEvent& e)
1234 platform::set_modal_pause(true);
1235 std::set<std::string> old_watches;
1236 runemufn([&old_watches]() { old_watches = get_watches(); });
1237 std::map<std::string, std::string> new_watches;
1238 std::string filename;
1240 wxFileDialog* d = new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1241 if(d->ShowModal() == wxID_CANCEL) {
1242 d->Destroy();
1243 platform::set_modal_pause(false);
1244 return;
1246 filename = tostdstring(d->GetPath());
1247 d->Destroy();
1248 //Did we pick a .zip file?
1249 try {
1250 zip_reader zr(filename);
1251 std::vector<wxString> files;
1252 for(auto i : zr)
1253 files.push_back(towxstring(i));
1254 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1255 wxT("Select member"), files.size(), &files[0]);
1256 if(d2->ShowModal() == wxID_CANCEL) {
1257 d2->Destroy();
1258 platform::set_modal_pause(false);
1259 return;
1261 filename = filename + "/" + tostdstring(d2->GetStringSelection());
1262 d2->Destroy();
1263 } catch(...) {
1264 //Ignore error.
1267 try {
1268 std::istream& in = open_file_relative(filename, "");
1269 while(in) {
1270 std::string wname;
1271 std::string wexpr;
1272 std::getline(in, wname);
1273 std::getline(in, wexpr);
1274 new_watches[wname] = wexpr;
1276 delete &in;
1277 } catch(std::exception& e) {
1278 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1279 "watch: ") + e.what()), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1280 d3->ShowModal();
1281 d3->Destroy();
1282 platform::set_modal_pause(false);
1283 return;
1286 runemufn([&new_watches, &old_watches]() {
1287 for(auto i : new_watches)
1288 set_watchexpr_for(i.first, i.second);
1289 for(auto i : old_watches)
1290 if(!new_watches.count(i))
1291 set_watchexpr_for(i, "");
1293 platform::set_modal_pause(false);
1296 void wxwin_mainwindow::menu_save_memorywatch(wxCommandEvent& e)
1298 platform::set_modal_pause(true);
1299 std::set<std::string> old_watches;
1300 runemufn([&old_watches]() { old_watches = get_watches(); });
1301 std::string filename;
1303 wxFileDialog* d = new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1304 if(d->ShowModal() == wxID_CANCEL) {
1305 d->Destroy();
1306 platform::set_modal_pause(false);
1307 return;
1309 filename = tostdstring(d->GetPath());
1310 d->Destroy();
1312 std::ofstream out(filename.c_str());
1313 for(auto i : old_watches)
1314 out << i << std::endl << get_watchexpr_for(i) << std::endl;
1315 out.close();
1316 platform::set_modal_pause(false);
1320 void wxwin_mainwindow::menu_edit_memorywatch(wxCommandEvent& e)
1322 platform::set_modal_pause(true);
1323 std::set<std::string> bind;
1324 runemufn([&bind]() { bind = get_watches(); });
1325 std::vector<wxString> choices;
1326 choices.push_back(wxT(NEW_WATCH));
1327 for(auto i : bind)
1328 choices.push_back(towxstring(i));
1329 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1330 wxT("Select watch"), choices.size(), &choices[0]);
1331 if(d->ShowModal() == wxID_CANCEL) {
1332 d->Destroy();
1333 platform::set_modal_pause(false);
1334 return;
1336 std::string watch = tostdstring(d->GetStringSelection());
1337 d->Destroy();
1338 if(watch == NEW_WATCH) {
1339 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1340 wxT("Enter watch name"));
1341 if(d2->ShowModal() == wxID_CANCEL) {
1342 d2->Destroy();
1343 platform::set_modal_pause(false);
1344 return;
1346 watch = tostdstring(d2->GetValue());
1347 d2->Destroy();
1349 std::string old_watch_value = get_watchexpr_for(watch);
1350 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1351 towxstring(old_watch_value), wxOK | wxCANCEL | wxCENTRE);
1352 if(d4->ShowModal() == wxID_CANCEL) {
1353 d4->Destroy();
1354 platform::set_modal_pause(false);
1355 return;
1357 std::string newexpr = tostdstring(d4->GetValue());
1358 runemufn([watch, newexpr]() { set_watchexpr_for(watch, newexpr); });
1359 platform::set_modal_pause(false);
1360 d4->Destroy();