Get rid of win32-crap.[ch]pp
[lsnes.git] / platform / wxwidgets / src / emufn.cpp
blob975f25eedf855f10808fc3df857e3eef63cf008e
1 #include "lsnes.hpp"
2 #include "emufn.hpp"
3 #include "zip.hpp"
4 #include "lua.hpp"
5 #include "keyentry.hpp"
6 #include "coroutine.hpp"
7 #include "moviedata.hpp"
8 #include "memorywatch.hpp"
9 #include "misc.hpp"
10 #include "command.hpp"
11 #include "controller.hpp"
12 #include "mainloop.hpp"
13 #include "keymapper.hpp"
14 #include "render.hpp"
15 #include "window.hpp"
16 #include "misc.hpp"
17 #include "framerate.hpp"
18 #include "common.hpp"
19 #include "moviedata.hpp"
20 #include "authorseditor.hpp"
21 #include "settingseditor.hpp"
22 #include "axeseditor.hpp"
23 #include <wx/wx.h>
24 #include <wx/event.h>
25 #include <wx/control.h>
26 #include <wx/combobox.h>
27 #include "status_window.hpp"
28 #include <cstdint>
29 extern "C"
31 #ifndef UINT64_C
32 #define UINT64_C(val) val##ULL
33 #endif
34 #include <libswscale/swscale.h>
36 #define STACKSIZE (8 * 1024 * 1024)
37 #define REQ_POLL_JOYSTICK 1
38 #define REQ_KEY_PRESS 2
39 #define REQ_KEY_RELEASE 3
40 #define REQ_CONTINUE 4
41 #define REQ_COMMAND 5
43 extern std::string lsnes_version;
45 enum
47 wxID_PAUSE = wxID_HIGHEST + 1,
48 wxID_FRAMEADVANCE,
49 wxID_SUBFRAMEADVANCE,
50 wxID_NEXTPOLL,
51 wxID_ERESET,
52 wxID_AUDIO_ENABLED,
53 wxID_SHOW_AUDIO_STATUS,
54 wxID_AUDIODEV_FIRST,
55 wxID_AUDIODEV_LAST = wxID_AUDIODEV_FIRST + 255,
56 wxID_SAVE_STATE,
57 wxID_SAVE_MOVIE,
58 wxID_LOAD_STATE,
59 wxID_LOAD_STATE_RO,
60 wxID_LOAD_STATE_RW,
61 wxID_LOAD_STATE_P,
62 wxID_LOAD_MOVIE,
63 wxID_RUN_SCRIPT,
64 wxID_RUN_LUA,
65 wxID_EVAL_LUA,
66 wxID_SAVE_SCREENSHOT,
67 wxID_READONLY_MODE,
68 wxID_EDIT_AUTHORS,
69 wxID_AUTOHOLD_FIRST,
70 wxID_AUTOHOLD_LAST = wxID_AUTOHOLD_FIRST + 127,
71 wxID_EDIT_AXES,
72 wxID_EDIT_SETTINGS,
73 wxID_EDIT_KEYBINDINGS,
74 wxID_EDIT_ALIAS,
75 wxID_EDIT_MEMORYWATCH,
76 wxID_SAVE_MEMORYWATCH,
77 wxID_LOAD_MEMORYWATCH,
78 wxID_DUMP_AVICSCD,
79 wxID_END_AVICSCD,
80 wxID_DUMP_JMD,
81 wxID_END_JMD,
82 wxID_DUMP_SDMP,
83 wxID_END_SDMP,
86 #define MAXCONTROLLERS 8
87 #define CONTROLS_COUNT 16
89 class controller_autohold_menu : public wxMenu
91 public:
92 controller_autohold_menu(unsigned lid, enum devicetype_t dtype);
93 void change_type(enum devicetype_t dtype);
94 bool is_dummy();
95 void on_select(wxCommandEvent& e);
96 void update(unsigned pid, unsigned ctrlnum, bool newstate);
97 private:
98 unsigned our_lid;
99 devicetype_t devtype;
100 wxMenuItem* entries[CONTROLS_COUNT];
103 class autohold_menu : public wxMenu
105 public:
106 autohold_menu();
107 void reconfigure();
108 void on_select(wxCommandEvent& e);
109 void update(unsigned pid, unsigned ctrlnum, bool newstate);
110 private:
111 controller_autohold_menu* menus[MAXCONTROLLERS];
112 wxMenuItem* entries[MAXCONTROLLERS];
115 controller_autohold_menu::controller_autohold_menu(unsigned lid, enum devicetype_t dtype)
117 our_lid = lid;
118 devtype = DT_NONE;
119 for(unsigned i = 0; i < CONTROLS_COUNT; i++) {
120 int id = wxID_AUTOHOLD_FIRST + CONTROLS_COUNT * lid + i;
121 entries[i] = AppendCheckItem(id, towxstring(get_button_name(i)));
123 change_type(dtype);
126 void controller_autohold_menu::change_type(enum devicetype_t dtype)
128 int pid = controller_index_by_logical(our_lid);
129 for(unsigned i = 0; i < CONTROLS_COUNT; i++) {
130 int pidx = get_physcial_id_for_control(dtype, i);
131 if(pidx >= 0) {
132 entries[i]->Check(pid > 0 && get_autohold(pid, pidx));
133 entries[i]->Enable();
134 } else {
135 entries[i]->Check(false);
136 entries[i]->Enable(false);
139 devtype = dtype;
142 bool controller_autohold_menu::is_dummy()
144 return (devtype == DT_NONE);
147 void controller_autohold_menu::on_select(wxCommandEvent& e)
149 int x = e.GetId();
150 if(x < wxID_AUTOHOLD_FIRST + our_lid * CONTROLS_COUNT || x >= wxID_AUTOHOLD_FIRST * (our_lid + 1) *
151 CONTROLS_COUNT) {
152 return;
154 unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % CONTROLS_COUNT;
155 int pidx = get_physcial_id_for_control(devtype, lidx);
156 int pid = controller_index_by_logical(our_lid);
157 if(pid < 0 || pidx < 0 || !entries[lidx]) {
158 return;
160 //Autohold change on pid=pid, ctrlindx=idx, state
161 bool newstate = entries[lidx]->IsChecked();
162 change_autohold(pid, pidx, newstate);
165 void controller_autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
167 int pid2 = controller_index_by_logical(our_lid);
168 if(pid2 < 0 || static_cast<unsigned>(pid) != pid2)
169 return;
170 for(unsigned i = 0; i < CONTROLS_COUNT; i++) {
171 int idx = get_physcial_id_for_control(devtype, i);
172 if(idx < 0 || static_cast<unsigned>(idx) != ctrlnum)
173 continue;
174 entries[i]->Check(newstate);
178 autohold_menu::autohold_menu()
180 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
181 std::ostringstream str;
182 str << "Controller #&" << (i + 1);
183 menus[i] = new controller_autohold_menu(i, DT_NONE);
184 entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
185 entries[i]->Enable(!menus[i]->is_dummy());
187 reconfigure();
190 void autohold_menu::reconfigure()
192 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
193 menus[i]->change_type(controller_type_by_logical(i));
194 entries[i]->Enable(!menus[i]->is_dummy());
198 void autohold_menu::on_select(wxCommandEvent& e)
200 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
201 menus[i]->on_select(e);
204 void autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
206 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
207 menus[i]->update(pid, ctrlnum, newstate);
211 class emulator_main_window;
213 class sound_listener : public window_callback
215 public:
216 sound_listener(emulator_main_window* w);
217 ~sound_listener() throw();
218 void on_sound_unmute(bool unmute) throw();
219 void on_sound_change(const std::string& dev) throw();
220 void on_mode_change(bool readonly) throw();
221 void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
222 void on_autohold_reconfigure();
223 private:
224 emulator_main_window* win;
227 class emulator_main_panel : public wxPanel
229 public:
230 emulator_main_panel(wxWindow* win);
231 void on_paint(wxPaintEvent& e);
232 void on_erase(wxEraseEvent& e);
233 void on_keyboard_down(wxKeyEvent& e);
234 void on_keyboard_up(wxKeyEvent& e);
235 void on_mouse(wxMouseEvent& e);
238 class emulator_main_window : public wxFrame
240 public:
241 emulator_main_window(const std::string& name);
242 ~emulator_main_window();
243 void on_idle(wxIdleEvent& e);
244 void on_close(wxCloseEvent& e);
245 void menu_pause(wxCommandEvent& e);
246 void menu_frameadvance(wxCommandEvent& e);
247 void menu_exit(wxCommandEvent& e);
248 void menu_subframeadvance(wxCommandEvent& e);
249 void menu_nextpoll(wxCommandEvent& e);
250 void menu_reset(wxCommandEvent& e);
251 void menu_audio_enable(wxCommandEvent& e);
252 void menu_audio_status(wxCommandEvent& e);
253 void menu_choose_audio_device(wxCommandEvent& e);
254 void menu_loadsave(wxCommandEvent& e);
255 void menu_scripting(wxCommandEvent& e);
256 void menu_readonly(wxCommandEvent& e);
257 void menu_edit_authors(wxCommandEvent& e);
258 void menu_edit_axes(wxCommandEvent& e);
259 void menu_edit_settings(wxCommandEvent& e);
260 void menu_edit_keybindings(wxCommandEvent& e);
261 void menu_edit_aliases(wxCommandEvent& e);
262 void menu_edit_memorywatch(wxCommandEvent& e);
263 void menu_load_memorywatch(wxCommandEvent& e);
264 void menu_save_memorywatch(wxCommandEvent& e);
265 void menu_handle_dump(wxCommandEvent& e);
266 void request_paint();
267 wxMenuItem* sound_enable;
268 wxMenuItem* readonly_enable;
269 autohold_menu* ahmenu;
270 private:
271 emulator_main_panel* gpanel;
272 sound_listener* slistener;
275 namespace
278 //Modifier table.
279 struct modifier_entry
281 int mod;
282 const char* name;
283 const char* lname;
284 modifier* allocated;
285 } modifiers[] = {
286 { wxMOD_ALT, "alt", NULL, NULL },
287 { wxMOD_CONTROL, "ctrl", NULL, NULL },
288 { wxMOD_SHIFT, "shift", NULL, NULL },
289 { wxMOD_META, "meta", NULL, NULL },
290 #ifdef __WXMAC__
291 { wxMOD_CMD, "cmd", NULL, NULL },
292 #endif
293 { 0, NULL, NULL }
296 struct key_entry
298 int keynum;
299 const char* name;
300 keygroup* allocated;
301 } keys[] = {
302 { WXK_BACK, "back", NULL },
303 { WXK_TAB, "tab", NULL },
304 { WXK_RETURN, "return", NULL },
305 { WXK_ESCAPE, "escape", NULL },
306 { WXK_SPACE, "space", NULL },
307 { 33, "exclaim", NULL },
308 { 34, "quotedbl", NULL },
309 { 35, "hash", NULL },
310 { 36, "dollar", NULL },
311 { 37, "percent", NULL },
312 { 38, "ampersand", NULL },
313 { 39, "quote", NULL },
314 { 40, "leftparen", NULL },
315 { 41, "rightparen", NULL },
316 { 42, "asterisk", NULL },
317 { 43, "plus", NULL },
318 { 44, "comma", NULL },
319 { 45, "minus", NULL },
320 { 46, "period", NULL },
321 { 47, "slash", NULL },
322 { 48, "0", NULL },
323 { 49, "1", NULL },
324 { 50, "2", NULL },
325 { 51, "3", NULL },
326 { 52, "4", NULL },
327 { 53, "5", NULL },
328 { 54, "6", NULL },
329 { 55, "7", NULL },
330 { 56, "8", NULL },
331 { 57, "9", NULL },
332 { 58, "colon", NULL },
333 { 59, "semicolon", NULL },
334 { 60, "less", NULL },
335 { 61, "equals", NULL },
336 { 62, "greater", NULL },
337 { 63, "question", NULL },
338 { 64, "at", NULL },
339 { 65, "a", NULL },
340 { 66, "b", NULL },
341 { 67, "c", NULL },
342 { 68, "d", NULL },
343 { 69, "e", NULL },
344 { 70, "f", NULL },
345 { 71, "g", NULL },
346 { 72, "h", NULL },
347 { 73, "i", NULL },
348 { 74, "j", NULL },
349 { 75, "k", NULL },
350 { 76, "l", NULL },
351 { 77, "m", NULL },
352 { 78, "n", NULL },
353 { 79, "o", NULL },
354 { 80, "p", NULL },
355 { 81, "q", NULL },
356 { 82, "r", NULL },
357 { 83, "s", NULL },
358 { 84, "t", NULL },
359 { 85, "u", NULL },
360 { 86, "v", NULL },
361 { 87, "w", NULL },
362 { 88, "x", NULL },
363 { 89, "y", NULL },
364 { 90, "z", NULL },
365 { 91, "leftbracket", NULL },
366 { 92, "backslash", NULL },
367 { 93, "rightbracket", NULL },
368 { 94, "caret", NULL },
369 { 95, "underscore", NULL },
370 { 96, "backquote", NULL },
371 { 97, "a", NULL },
372 { 98, "b", NULL },
373 { 99, "c", NULL },
374 { 100, "d", NULL },
375 { 101, "e", NULL },
376 { 102, "f", NULL },
377 { 103, "g", NULL },
378 { 104, "h", NULL },
379 { 105, "i", NULL },
380 { 106, "j", NULL },
381 { 107, "k", NULL },
382 { 108, "l", NULL },
383 { 109, "m", NULL },
384 { 110, "n", NULL },
385 { 111, "o", NULL },
386 { 112, "p", NULL },
387 { 113, "q", NULL },
388 { 114, "r", NULL },
389 { 115, "s", NULL },
390 { 116, "t", NULL },
391 { 117, "u", NULL },
392 { 118, "v", NULL },
393 { 119, "w", NULL },
394 { 120, "x", NULL },
395 { 121, "y", NULL },
396 { 122, "z", NULL },
397 { 123, "leftcurly", NULL },
398 { 124, "pipe", NULL },
399 { 125, "rightcurly", NULL },
400 { 126, "tilde", NULL },
401 { WXK_DELETE, "delete", NULL },
402 { WXK_START, "start", NULL },
403 { WXK_LBUTTON, "lbutton", NULL },
404 { WXK_RBUTTON, "rbutton", NULL },
405 { WXK_CANCEL, "cancel", NULL },
406 { WXK_MBUTTON, "mbutton", NULL },
407 { WXK_CLEAR, "clear", NULL },
408 { WXK_SHIFT, "shift", NULL },
409 { WXK_ALT, "alt", NULL },
410 { WXK_CONTROL, "control", NULL },
411 { WXK_MENU, "menu", NULL },
412 { WXK_PAUSE, "pause", NULL },
413 { WXK_CAPITAL, "capital", NULL },
414 { WXK_END, "end", NULL },
415 { WXK_HOME, "home", NULL },
416 { WXK_LEFT, "lefT", NULL },
417 { WXK_UP, "up", NULL },
418 { WXK_RIGHT, "right", NULL },
419 { WXK_DOWN, "down", NULL },
420 { WXK_SELECT, "select", NULL },
421 { WXK_PRINT, "print", NULL },
422 { WXK_EXECUTE, "execute", NULL },
423 { WXK_SNAPSHOT, "snapshot", NULL },
424 { WXK_INSERT, "insert", NULL },
425 { WXK_HELP, "help", NULL },
426 { WXK_NUMPAD0, "numpad0", NULL },
427 { WXK_NUMPAD1, "numpad1", NULL },
428 { WXK_NUMPAD2, "numpad2", NULL },
429 { WXK_NUMPAD3, "numpad3", NULL },
430 { WXK_NUMPAD4, "numpad4", NULL },
431 { WXK_NUMPAD5, "numpad5", NULL },
432 { WXK_NUMPAD6, "numpad6", NULL },
433 { WXK_NUMPAD7, "numpad7", NULL },
434 { WXK_NUMPAD8, "numpad8", NULL },
435 { WXK_NUMPAD9, "numpad9", NULL },
436 { WXK_MULTIPLY, "multiply", NULL },
437 { WXK_ADD, "add", NULL },
438 { WXK_SEPARATOR, "separator", NULL },
439 { WXK_SUBTRACT, "subtract", NULL },
440 { WXK_DECIMAL, "decimal", NULL },
441 { WXK_DIVIDE, "divide", NULL },
442 { WXK_F1, "f1", NULL },
443 { WXK_F2, "f2", NULL },
444 { WXK_F3, "f3", NULL },
445 { WXK_F4, "f4", NULL },
446 { WXK_F5, "f5", NULL },
447 { WXK_F6, "f6", NULL },
448 { WXK_F7, "f7", NULL },
449 { WXK_F8, "f8", NULL },
450 { WXK_F9, "f9", NULL },
451 { WXK_F10, "f10", NULL },
452 { WXK_F11, "f11", NULL },
453 { WXK_F12, "f12", NULL },
454 { WXK_F13, "f13", NULL },
455 { WXK_F14, "f14", NULL },
456 { WXK_F15, "f15", NULL },
457 { WXK_F16, "f16", NULL },
458 { WXK_F17, "f17", NULL },
459 { WXK_F18, "f18", NULL },
460 { WXK_F19, "f19", NULL },
461 { WXK_F20, "f20", NULL },
462 { WXK_F21, "f21", NULL },
463 { WXK_F22, "f22", NULL },
464 { WXK_F23, "f23", NULL },
465 { WXK_F24, "f24", NULL },
466 { WXK_NUMLOCK, "numlock", NULL },
467 { WXK_SCROLL, "scroll", NULL },
468 { WXK_PAGEUP, "pageup", NULL },
469 { WXK_PAGEDOWN, "pagedown", NULL },
470 { WXK_NUMPAD_SPACE, "numpad_space", NULL },
471 { WXK_NUMPAD_TAB, "numpad_tab", NULL },
472 { WXK_NUMPAD_ENTER, "numpad_enter", NULL },
473 { WXK_NUMPAD_F1, "numpad_f1", NULL },
474 { WXK_NUMPAD_F2, "numpad_f2", NULL },
475 { WXK_NUMPAD_F3, "numpad_f3", NULL },
476 { WXK_NUMPAD_F4, "numpad_f4", NULL },
477 { WXK_NUMPAD_HOME, "numpad_home", NULL },
478 { WXK_NUMPAD_LEFT, "numpad_left", NULL },
479 { WXK_NUMPAD_UP, "numpad_up", NULL },
480 { WXK_NUMPAD_RIGHT, "numpad_right", NULL },
481 { WXK_NUMPAD_DOWN, "numpad_down", NULL },
482 { WXK_NUMPAD_PAGEUP, "numpad_pageup", NULL },
483 { WXK_NUMPAD_PAGEDOWN, "numpad_pagedown", NULL },
484 { WXK_NUMPAD_END, "numpad_end", NULL },
485 { WXK_NUMPAD_BEGIN, "numpad_begin", NULL },
486 { WXK_NUMPAD_INSERT, "numpad_insert", NULL },
487 { WXK_NUMPAD_DELETE, "numpad_delete", NULL },
488 { WXK_NUMPAD_EQUAL, "numpad_equal", NULL },
489 { WXK_NUMPAD_MULTIPLY, "numpad_multiply", NULL },
490 { WXK_NUMPAD_ADD, "numpad_add", NULL },
491 { WXK_NUMPAD_SEPARATOR, "numpad_separator", NULL },
492 { WXK_NUMPAD_SUBTRACT, "numpad_subtract", NULL },
493 { WXK_NUMPAD_DECIMAL, "numpad_decimal", NULL },
494 { WXK_NUMPAD_DIVIDE, "numpad_divide", NULL },
495 { WXK_WINDOWS_LEFT, "windows_left", NULL },
496 { WXK_WINDOWS_RIGHT, "windows_right", NULL },
497 { WXK_WINDOWS_MENU, "windows_menu", NULL },
498 { WXK_COMMAND, "command", NULL },
499 { WXK_SPECIAL1, "special1", NULL },
500 { WXK_SPECIAL2, "special2", NULL },
501 { WXK_SPECIAL3, "special3", NULL },
502 { WXK_SPECIAL4, "special4", NULL },
503 { WXK_SPECIAL5, "special5", NULL },
504 { WXK_SPECIAL6, "special6", NULL },
505 { WXK_SPECIAL7, "special7", NULL },
506 { WXK_SPECIAL8, "special8", NULL },
507 { WXK_SPECIAL9, "special9", NULL },
508 { WXK_SPECIAL10, "special10", NULL },
509 { WXK_SPECIAL11, "special11", NULL },
510 { WXK_SPECIAL12, "special12", NULL },
511 { WXK_SPECIAL13, "special13", NULL },
512 { WXK_SPECIAL14, "special14", NULL },
513 { WXK_SPECIAL15, "special15", NULL },
514 { WXK_SPECIAL16, "special16", NULL },
515 { WXK_SPECIAL17, "special17", NULL },
516 { WXK_SPECIAL18, "special18", NULL },
517 { WXK_SPECIAL19, "special19", NULL },
518 { WXK_SPECIAL20, "special20", NULL },
519 { 0, NULL, NULL }
522 std::map<int, modifier*> modifier_map;
523 std::map<int, keygroup*> key_map;
524 std::map<std::string, int> keys_allocated;
525 std::set<int> keys_held;
527 void init_modifiers_and_keys()
529 static bool done = false;
530 if(done)
531 return;
532 modifier_entry* m = modifiers;
533 while(m->name) {
534 if(m->lname)
535 m->allocated = new modifier(m->name, m->lname);
536 else
537 m->allocated = new modifier(m->name);
538 modifier_map[m->mod] = m->allocated;
539 m++;
541 key_entry* k = keys;
542 while(k->name) {
543 if(!keys_allocated.count(k->name)) {
544 k->allocated = new keygroup(k->name, keygroup::KT_KEY);
545 key_map[k->keynum] = k->allocated;
546 keys_allocated[k->name] = k->keynum;
547 } else
548 key_map[k->keynum] = key_map[keys_allocated[k->name]];
549 k++;
551 done = true;
554 //The coroutine emulator itself runs in.
555 coroutine* emu_cr;
556 emulator_main_window* main_window = NULL;
558 struct emulator_boot_state
560 loaded_rom* rom;
561 moviefile* movie;
564 void emulator_bootup_fn(void* state)
566 try {
567 struct emulator_boot_state* boot_state = reinterpret_cast<struct emulator_boot_state*>(state);
568 main_loop(*boot_state->rom, *boot_state->movie);
569 } catch(std::bad_alloc& e) {
570 OOM_panic();
571 } catch(std::exception& e) {
572 messages << "FATAL: " << e.what() << std::endl;
573 fatal_error();
574 return;
578 void show_fps()
580 auto& emustatus = window::get_emustatus();
581 try {
582 std::ostringstream y;
583 y << get_framerate();
584 emustatus["FPS"] = y.str();
585 } catch(...) {
589 //Set to true if the emulator is in paused mode.
590 volatile bool e_paused;
591 //Set to true if the emulator is in waiting mode. wait_until is set to get_utime() time when emulator is
592 //to exit the wait.
593 volatile bool waiting;
594 volatile uint64_t wait_until;
595 //Set to true if the screen needs updating. screen_updated_full is set if update needs to be full.
596 volatile bool screen_updated;
597 volatile bool screen_updated_full;
598 //The backrequest type.
599 volatile int request;
600 //The modifier_set and the key (for REQ_KEY_PRESS and REQ_KEY_RELEASE).
601 modifier_set keypress_modifiers;
602 keygroup* presed_key;
603 //Command to send (for REQ_COMMAND).
604 std::string pending_command;
605 //The main screen
606 screen* main_screen;
607 unsigned char* screen_buffer;
608 uint32_t old_width = 0;
609 uint32_t old_height = 0;
610 //Message queue (undisplayed messages), and last message.
611 bool messages_need_painting;
612 std::string last_message;
613 //In modal dialog flag.
614 bool in_modal_dialog;
615 //Painting.
616 bool main_window_dirty = false;
617 //Audio devices.
618 std::map<int, std::string> audio_devs;
619 std::map<int, wxMenuItem*> audio_devitems;
621 void handle_idle(wxIdleEvent& e)
623 if(!emu_cr || in_modal_dialog) {
624 wxMilliSleep(1);
625 e.RequestMore();
626 return;
628 request = REQ_POLL_JOYSTICK;
629 emu_cr->resume();
630 if(e_paused || (waiting && wait_until > get_utime())) {
631 wxMilliSleep(1);
632 e.RequestMore();
633 return;
635 if(waiting)
636 waiting = false;
637 request = REQ_CONTINUE;
638 uint64_t exec_start = get_utime();
639 loop:
640 emu_cr->resume();
641 if(emu_cr->is_dead()) {
642 //Bye!
643 if(window1)
644 window1->Destroy();
645 if(window2)
646 window2->Destroy();
647 delete emu_cr;
648 delete our_rom;
649 our_rom = NULL;
650 main_window->Destroy();
651 return;
653 if(get_utime() < exec_start + 10000 && !e_paused && !waiting)
654 goto loop;
655 show_fps();
656 e.RequestMore();
659 //Request keypress event to happen.
660 void do_keypress(modifier_set mods, keygroup& key, bool polarity)
662 if(!emu_cr)
663 return;
664 keypress_modifiers = mods;
665 presed_key = &key;
666 request = polarity ? REQ_KEY_PRESS : REQ_KEY_RELEASE;
667 emu_cr->resume();
670 void handle_wx_keyboard(wxKeyEvent& e, bool polarity)
672 int mods = e.GetModifiers();
673 int keyc = e.GetKeyCode();
674 modifier_set mset;
675 modifier_entry* m = modifiers;
676 while(m->name) {
677 if((keyc & m->mod) == m->mod) {
678 mset.add(*m->allocated);
680 m++;
682 if(polarity) {
683 if(keys_held.count(keyc)) {
684 e.Skip();
685 return;
687 keys_held.insert(keyc);
688 } else
689 keys_held.erase(keyc);
690 key_entry* k = keys;
691 keygroup* grp = NULL;
692 while(k->name) {
693 if(k->keynum == keyc) {
694 grp = k->allocated;
695 break;
697 k++;
699 if(grp)
700 do_keypress(mset, *grp, polarity);
701 e.Skip();
704 void handle_wx_mouse(wxMouseEvent& e)
706 static uint32_t mask = 0;
707 if(e.LeftDown())
708 mask |= 1;
709 if(e.LeftUp())
710 mask &= ~1;
711 if(e.MiddleDown())
712 mask |= 2;
713 if(e.MiddleUp())
714 mask &= ~2;
715 if(e.RightDown())
716 mask |= 4;
717 if(e.RightUp())
718 mask &= ~4;
719 window_callback::do_click(e.GetX(), e.GetY(), mask);
723 void emulator_main_panel::on_keyboard_down(wxKeyEvent& e)
725 handle_wx_keyboard(e, true);
728 void emulator_main_panel::on_keyboard_up(wxKeyEvent& e)
730 handle_wx_keyboard(e, false);
733 void emulator_main_panel::on_mouse(wxMouseEvent& e)
735 handle_wx_mouse(e);
739 emulator_main_panel::emulator_main_panel(wxWindow* win)
740 : wxPanel(win)
742 this->Connect(wxEVT_PAINT, wxPaintEventHandler(emulator_main_panel::on_paint), NULL, this);
743 this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(emulator_main_panel::on_erase), NULL, this);
744 this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(emulator_main_panel::on_keyboard_down), NULL, this);
745 this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(emulator_main_panel::on_keyboard_up), NULL, this);
746 this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
747 this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
748 this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
749 this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
750 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
751 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(emulator_main_panel::on_mouse), NULL, this);
752 SetMinSize(wxSize(512, 448));
755 void emulator_main_panel::on_paint(wxPaintEvent& e)
757 static struct SwsContext* ctx;
758 uint8_t* srcp[1];
759 int srcs[1];
760 uint8_t* dstp[1];
761 int dsts[1];
762 wxPaintDC dc(this);
763 if(!main_screen)
764 return;
765 if(main_screen->width != old_width || main_screen->height != old_height) {
766 delete[] screen_buffer;
767 screen_buffer = new unsigned char[main_screen->width * main_screen->height * 3];
768 old_height = main_screen->height;
769 old_width = main_screen->width;
770 uint32_t w = main_screen->width;
771 uint32_t h = main_screen->height;
772 ctx = sws_getCachedContext(ctx, w, h, PIX_FMT_RGBA, w, h, PIX_FMT_BGR24, SWS_POINT |
773 SWS_CPU_CAPS_MMX2, NULL, NULL, NULL);
774 if(w < 512)
775 w = 512;
776 if(h < 448)
777 h = 448;
778 SetMinSize(wxSize(w, h));
780 srcs[0] = 4 * main_screen->width;
781 dsts[0] = 3 * main_screen->width;
782 srcp[0] = reinterpret_cast<unsigned char*>(main_screen->memory);
783 dstp[0] = screen_buffer;
784 memset(screen_buffer, 0, main_screen->width * main_screen->height * 3);
785 uint64_t t1 = get_utime();
786 sws_scale(ctx, srcp, srcs, 0, main_screen->height, dstp, dsts);
787 uint64_t t2 = get_utime();
788 wxBitmap bmp(wxImage(main_screen->width, main_screen->height, screen_buffer, true));
789 uint64_t t3 = get_utime();
790 dc.DrawBitmap(bmp, 0, 0, false);
791 main_window_dirty = false;
794 void emulator_main_panel::on_erase(wxEraseEvent& e)
798 emulator_main_window::emulator_main_window(const std::string& name)
799 : wxFrame(NULL, wxID_ANY, towxstring(name), wxDefaultPosition, wxSize(-1, -1), primary_window_style)
801 Centre();
802 wxFlexGridSizer* top_s = new wxFlexGridSizer(1, 1, 0, 0);
803 top_s->Add(gpanel = new emulator_main_panel(this), 1, wxGROW);
804 top_s->SetSizeHints(this);
805 SetSizer(top_s);
806 Fit();
807 Connect(wxEVT_IDLE, wxIdleEventHandler(emulator_main_window::on_idle));
808 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(emulator_main_window::on_close));
809 wxIdleEvent event;
810 event.SetEventObject(this);
811 AddPendingEvent(event);
812 gpanel->SetFocus();
814 wxMenuBar* menubar = new wxMenuBar;
815 wxMenu* system = new wxMenu;
816 wxMenu* sound = new wxMenu;
817 wxMenu* file = new wxMenu;
818 wxMenu* scripting = new wxMenu;
819 wxMenu* settings = new wxMenu;
820 wxMenu* dump = new wxMenu;
821 ahmenu = new autohold_menu();
822 SetMenuBar(menubar);
823 //Main menubar: ACFOS
824 menubar->Append(system, wxT("&System"));
825 menubar->Append(file, wxT("&File"));
826 menubar->Append(ahmenu, wxT("&Autohold"));
827 menubar->Append(scripting, wxT("S&cripting"));
828 menubar->Append(settings, wxT("Settings"));
829 if(window::sound_initialized())
830 menubar->Append(sound, wxT("S&ound"));
831 //System menu: EMNPQRU
832 system->Append(wxID_FRAMEADVANCE, wxT("Fra&me advance"));
833 system->Append(wxID_SUBFRAMEADVANCE, wxT("S&ubframe advance"));
834 system->Append(wxID_NEXTPOLL, wxT("&Next poll"));
835 system->Append(wxID_PAUSE, wxT("&Pause/Unpause"));
836 system->AppendSeparator();
837 system->Append(wxID_ERESET, wxT("&Reset"));
838 system->AppendSeparator();
839 system->Append(wxID_EDIT_AUTHORS, wxT("&Edit game name && authors"));
840 system->AppendSeparator();
841 system->Append(wxID_EXIT, wxT("&Quit"));
842 //File menu: DELMNPRTV
843 readonly_enable = file->AppendCheckItem(wxID_READONLY_MODE, wxT("Reado&nly mode"));
844 readonly_enable->Check(movb.get_movie().readonly_mode());
845 file->AppendSeparator();
846 file->Append(wxID_SAVE_STATE, wxT("Save stat&e"));
847 file->Append(wxID_SAVE_MOVIE, wxT("Sa&ve movie"));
848 file->AppendSeparator();
849 file->Append(wxID_LOAD_STATE, wxT("&Load state"));
850 file->Append(wxID_LOAD_STATE_RO, wxT("Loa&d state (readonly)"));
851 file->Append(wxID_LOAD_STATE_RW, wxT("Load s&tate (read-write)"));
852 file->Append(wxID_LOAD_STATE_P, wxT("Load state (&preserve)"));
853 file->Append(wxID_LOAD_MOVIE, wxT("Load &movie"));
854 file->AppendSeparator();
855 file->Append(wxID_SAVE_SCREENSHOT, wxT("Save sc&reenshot"));
856 file->AppendSeparator();
857 file->AppendSubMenu(dump, wxT("Video dump"));
858 dump->Append(wxID_DUMP_AVICSCD, wxT("Dump AVI(CSCD)"));
859 dump->Append(wxID_END_AVICSCD, wxT("End AVI(CSCD) dump"));
860 dump->AppendSeparator();
861 dump->Append(wxID_DUMP_JMD, wxT("Dump JMD"));
862 dump->Append(wxID_END_JMD, wxT("End JMD dump"));
863 dump->AppendSeparator();
864 dump->Append(wxID_DUMP_SDMP, wxT("Dump SDMP"));
865 dump->Append(wxID_END_SDMP, wxT("End SDMP dump"));
866 //Scripting menu: ERU
867 scripting->Append(wxID_RUN_SCRIPT, wxT("&Run script"));
868 if(lua_supported) {
869 scripting->AppendSeparator();
870 scripting->Append(wxID_EVAL_LUA, wxT("&Evaluate Lua statement"));
871 scripting->Append(wxID_RUN_LUA, wxT("R&un Lua script"));
873 scripting->AppendSeparator();
874 scripting->Append(wxID_EDIT_MEMORYWATCH, wxT("Edit memory watch"));
875 scripting->AppendSeparator();
876 scripting->Append(wxID_LOAD_MEMORYWATCH, wxT("Load memory watch"));
877 scripting->Append(wxID_SAVE_MEMORYWATCH, wxT("Save memory watch"));
878 //Settings menu.
879 settings->Append(wxID_EDIT_AXES, wxT("Configure axes"));
880 settings->Append(wxID_EDIT_SETTINGS, wxT("Configure settings"));
881 settings->Append(wxID_EDIT_KEYBINDINGS, wxT("Configure keybindings"));
882 settings->Append(wxID_EDIT_ALIAS, wxT("Configure aliases"));
883 //Sound menu U
884 sound_enable = NULL;
885 if(window::sound_initialized()) {
886 slistener = new sound_listener(this);
887 sound_enable = sound->AppendCheckItem(wxID_AUDIO_ENABLED, wxT("So&unds enabled"));
888 sound_enable->Check(window::is_sound_enabled());
889 sound->Append(wxID_SHOW_AUDIO_STATUS, wxT("Show audio status"));
890 sound->AppendSeparator();
891 int j = wxID_AUDIODEV_FIRST;
892 std::string curdev = window::get_current_sound_device();
893 for(auto i : window::get_sound_devices()) {
894 audio_devitems[j] = sound->AppendRadioItem(j, towxstring(i.first + "(" + i.second + ")"));
895 audio_devs[j] = i.first;
896 if(i.first == curdev)
897 audio_devitems[j]->Check();
898 j++;
902 menu_action(this, wxID_PAUSE, &emulator_main_window::menu_pause);
903 menu_action(this, wxID_FRAMEADVANCE, &emulator_main_window::menu_frameadvance);
904 menu_action(this, wxID_SUBFRAMEADVANCE, &emulator_main_window::menu_subframeadvance);
905 menu_action(this, wxID_NEXTPOLL, &emulator_main_window::menu_nextpoll);
906 menu_action(this, wxID_ERESET, &emulator_main_window::menu_reset);
907 menu_action(this, wxID_EXIT, &emulator_main_window::menu_exit);
908 menu_action(this, wxID_READONLY_MODE, &emulator_main_window::menu_readonly);
909 menu_action(this, wxID_SAVE_STATE, &emulator_main_window::menu_loadsave);
910 menu_action(this, wxID_SAVE_MOVIE, &emulator_main_window::menu_loadsave);
911 menu_action(this, wxID_LOAD_STATE, &emulator_main_window::menu_loadsave);
912 menu_action(this, wxID_LOAD_STATE_RO, &emulator_main_window::menu_loadsave);
913 menu_action(this, wxID_LOAD_STATE_RW, &emulator_main_window::menu_loadsave);
914 menu_action(this, wxID_LOAD_STATE_P, &emulator_main_window::menu_loadsave);
915 menu_action(this, wxID_LOAD_MOVIE, &emulator_main_window::menu_loadsave);
916 menu_action(this, wxID_SAVE_SCREENSHOT, &emulator_main_window::menu_loadsave);
917 menu_action(this, wxID_DUMP_AVICSCD, &emulator_main_window::menu_handle_dump);
918 menu_action(this, wxID_DUMP_JMD, &emulator_main_window::menu_handle_dump);
919 menu_action(this, wxID_DUMP_SDMP, &emulator_main_window::menu_handle_dump);
920 menu_action(this, wxID_END_AVICSCD, &emulator_main_window::menu_handle_dump);
921 menu_action(this, wxID_END_JMD, &emulator_main_window::menu_handle_dump);
922 menu_action(this, wxID_END_SDMP, &emulator_main_window::menu_handle_dump);
923 menu_action(this, wxID_RUN_SCRIPT, &emulator_main_window::menu_scripting);
924 menu_action(this, wxID_EDIT_AUTHORS, &emulator_main_window::menu_edit_authors);
925 Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED,
926 wxCommandEventHandler(autohold_menu::on_select), NULL, ahmenu);
927 if(lua_supported) {
928 menu_action(this, wxID_EVAL_LUA, &emulator_main_window::menu_scripting);
929 menu_action(this, wxID_RUN_LUA, &emulator_main_window::menu_scripting);
931 menu_action(this, wxID_EDIT_MEMORYWATCH, &emulator_main_window::menu_edit_memorywatch);
932 menu_action(this, wxID_LOAD_MEMORYWATCH, &emulator_main_window::menu_load_memorywatch);
933 menu_action(this, wxID_SAVE_MEMORYWATCH, &emulator_main_window::menu_save_memorywatch);
934 menu_action(this, wxID_EDIT_AXES, &emulator_main_window::menu_edit_axes);
935 menu_action(this, wxID_EDIT_SETTINGS, &emulator_main_window::menu_edit_settings);
936 menu_action(this, wxID_EDIT_KEYBINDINGS, &emulator_main_window::menu_edit_keybindings);
937 menu_action(this, wxID_EDIT_ALIAS, &emulator_main_window::menu_edit_aliases);
938 if(window::sound_initialized()) {
939 menu_action(this, wxID_AUDIO_ENABLED, &emulator_main_window::menu_audio_enable);
940 menu_action(this, wxID_SHOW_AUDIO_STATUS, &emulator_main_window::menu_audio_status);
941 for(auto i : audio_devs)
942 menu_action(this, i.first, &emulator_main_window::menu_choose_audio_device);
946 emulator_main_window::~emulator_main_window()
948 delete slistener;
951 void emulator_main_window::request_paint()
953 gpanel->Refresh();
956 void emulator_main_window::on_idle(wxIdleEvent& e)
958 handle_idle(e);
961 void emulator_main_window::on_close(wxCloseEvent& e)
963 //Veto it for now, latter things will delete it.
964 e.Veto();
965 exec_command("quit-emulator");
968 void emulator_main_window::menu_pause(wxCommandEvent& e)
970 exec_command("pause-emulator");
973 void emulator_main_window::menu_frameadvance(wxCommandEvent& e)
975 exec_command("+advance-frame");
976 exec_command("-advance-frame");
979 void emulator_main_window::menu_subframeadvance(wxCommandEvent& e)
981 exec_command("+advance-poll");
982 exec_command("-advance-poll");
985 void emulator_main_window::menu_nextpoll(wxCommandEvent& e)
987 exec_command("advance-skiplag");
990 void emulator_main_window::menu_reset(wxCommandEvent& e)
992 exec_command("reset");
995 void emulator_main_window::menu_exit(wxCommandEvent& e)
997 exec_command("quit-emulator");
1000 void emulator_main_window::menu_audio_enable(wxCommandEvent& e)
1002 window::sound_enable(sound_enable->IsChecked());
1005 void emulator_main_window::menu_readonly(wxCommandEvent& e)
1007 bool s = readonly_enable->IsChecked();
1008 movb.get_movie().readonly_mode(s);
1009 if(!s)
1010 lua_callback_do_readwrite();
1011 update_movie_state();
1012 window::notify_screen_update();
1015 void emulator_main_window::menu_edit_authors(wxCommandEvent& e)
1017 wxDialog* editor = new wx_authors_editor(this);
1018 editor->ShowModal();
1019 editor->Destroy();
1022 void emulator_main_window::menu_edit_axes(wxCommandEvent& e)
1024 wxDialog* editor = new wx_axes_editor(this);
1025 editor->ShowModal();
1026 editor->Destroy();
1029 void emulator_main_window::menu_edit_settings(wxCommandEvent& e)
1031 wxDialog* editor = new wx_settings_editor(this);
1032 editor->ShowModal();
1033 editor->Destroy();
1036 #define NEW_KEYBINDING "A new binding..."
1037 #define NEW_ALIAS "A new alias..."
1038 #define NEW_WATCH "A new watch..."
1040 void emulator_main_window::menu_edit_keybindings(wxCommandEvent& e)
1042 std::set<std::string> bind = keymapper::get_bindings();
1043 std::vector<wxString> choices;
1044 choices.push_back(wxT(NEW_KEYBINDING));
1045 for(auto i : bind)
1046 choices.push_back(towxstring(i));
1047 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1048 wxT("Select binding"), choices.size(), &choices[0]);
1049 if(d->ShowModal() == wxID_CANCEL) {
1050 d->Destroy();
1051 return;
1053 std::string key = tostdstring(d->GetStringSelection());
1054 d->Destroy();
1055 if(key == NEW_KEYBINDING) {
1056 wx_key_entry* d2 = new wx_key_entry(this);
1057 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1058 // wxT("Edit binding"), wxT(""));
1059 if(d2->ShowModal() == wxID_CANCEL) {
1060 d2->Destroy();
1061 return;
1063 key = d2->getkey();
1064 //key = tostdstring(d2->GetValue());
1065 d2->Destroy();
1067 std::string old_command_value = keymapper::get_command_for(key);
1068 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1069 towxstring(old_command_value));
1070 if(d4->ShowModal() == wxID_CANCEL) {
1071 d4->Destroy();
1072 return;
1074 try {
1075 keymapper::bind_for(key, tostdstring(d4->GetValue()));
1076 } catch(std::exception& e) {
1077 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1078 e.what()), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1079 d3->ShowModal();
1080 d3->Destroy();
1082 d4->Destroy();
1085 void emulator_main_window::menu_edit_aliases(wxCommandEvent& e)
1087 std::set<std::string> bind = command::get_aliases();
1088 std::vector<wxString> choices;
1089 choices.push_back(wxT(NEW_ALIAS));
1090 for(auto i : bind)
1091 choices.push_back(towxstring(i));
1092 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1093 wxT("Select alias"), choices.size(), &choices[0]);
1094 if(d->ShowModal() == wxID_CANCEL) {
1095 d->Destroy();
1096 return;
1098 std::string alias = tostdstring(d->GetStringSelection());
1099 d->Destroy();
1100 if(alias == NEW_ALIAS) {
1101 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1102 wxT("Enter alias name"));
1103 if(d2->ShowModal() == wxID_CANCEL) {
1104 d2->Destroy();
1105 return;
1107 alias = tostdstring(d2->GetValue());
1108 d2->Destroy();
1109 if(!command::valid_alias_name(alias)) {
1110 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1111 "name: ") + alias), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1112 d3->ShowModal();
1113 d3->Destroy();
1114 return;
1117 std::string old_alias_value = command::get_alias_for(alias);
1118 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1119 towxstring(old_alias_value), wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE);
1120 if(d4->ShowModal() == wxID_CANCEL) {
1121 d4->Destroy();
1122 return;
1124 command::set_alias_for(alias, tostdstring(d4->GetValue()));
1125 d4->Destroy();
1128 void emulator_main_window::menu_load_memorywatch(wxCommandEvent& e)
1130 std::set<std::string> old_watches = get_watches();
1131 std::map<std::string, std::string> new_watches;
1132 std::string filename;
1134 wxFileDialog* d = new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1135 if(d->ShowModal() == wxID_CANCEL) {
1136 d->Destroy();
1137 return;
1139 filename = tostdstring(d->GetPath());
1140 d->Destroy();
1141 //Did we pick a .zip file?
1142 try {
1143 zip_reader zr(filename);
1144 std::vector<wxString> files;
1145 for(auto i : zr)
1146 files.push_back(towxstring(i));
1147 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1148 wxT("Select member"), files.size(), &files[0]);
1149 if(d2->ShowModal() == wxID_CANCEL) {
1150 d2->Destroy();
1151 return;
1153 filename = filename + "/" + tostdstring(d2->GetStringSelection());
1154 d2->Destroy();
1155 } catch(...) {
1156 //Ignore error.
1159 try {
1160 std::istream& in = open_file_relative(filename, "");
1161 while(in) {
1162 std::string wname;
1163 std::string wexpr;
1164 std::getline(in, wname);
1165 std::getline(in, wexpr);
1166 new_watches[wname] = wexpr;
1168 delete &in;
1169 } catch(std::exception& e) {
1170 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1171 "watch: ") + e.what()), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1172 d3->ShowModal();
1173 d3->Destroy();
1176 for(auto i : new_watches)
1177 set_watchexpr_for(i.first, i.second);
1178 for(auto i : old_watches)
1179 if(!new_watches.count(i))
1180 set_watchexpr_for(i, "");
1183 void emulator_main_window::menu_save_memorywatch(wxCommandEvent& e)
1185 std::set<std::string> old_watches = get_watches();
1186 std::string filename;
1188 wxFileDialog* d = new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1189 if(d->ShowModal() == wxID_CANCEL) {
1190 d->Destroy();
1191 return;
1193 filename = tostdstring(d->GetPath());
1194 d->Destroy();
1196 std::ofstream out(filename.c_str());
1197 for(auto i : old_watches)
1198 out << i << std::endl << get_watchexpr_for(i) << std::endl;
1199 out.close();
1203 void emulator_main_window::menu_edit_memorywatch(wxCommandEvent& e)
1205 std::set<std::string> bind = get_watches();
1206 std::vector<wxString> choices;
1207 choices.push_back(wxT(NEW_WATCH));
1208 for(auto i : bind)
1209 choices.push_back(towxstring(i));
1210 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1211 wxT("Select watch"), choices.size(), &choices[0]);
1212 if(d->ShowModal() == wxID_CANCEL) {
1213 d->Destroy();
1214 return;
1216 std::string watch = tostdstring(d->GetStringSelection());
1217 d->Destroy();
1218 if(watch == NEW_WATCH) {
1219 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1220 wxT("Enter watch name"));
1221 if(d2->ShowModal() == wxID_CANCEL) {
1222 d2->Destroy();
1223 return;
1225 watch = tostdstring(d2->GetValue());
1226 d2->Destroy();
1228 std::string old_watch_value = get_watchexpr_for(watch);
1229 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1230 towxstring(old_watch_value), wxOK | wxCANCEL | wxCENTRE);
1231 if(d4->ShowModal() == wxID_CANCEL) {
1232 d4->Destroy();
1233 return;
1235 set_watchexpr_for(watch, tostdstring(d4->GetValue()));
1236 d4->Destroy();
1239 void emulator_main_window::menu_handle_dump(wxCommandEvent& e)
1241 wxString choices[19];
1242 size_t choice_count = 0;
1243 switch(e.GetId()) {
1244 case wxID_END_AVICSCD:
1245 exec_command("end-avi");
1246 return;
1247 case wxID_END_JMD:
1248 exec_command("end-jmd");
1249 return;
1250 case wxID_END_SDMP:
1251 exec_command("end-sdmp");
1252 return;
1253 default:
1254 break;
1256 bool is_prefix = false;
1257 bool has_level = false;
1258 std::string msg;
1259 std::string caption;
1260 std::string cmd;
1261 switch(e.GetId()) {
1262 case wxID_DUMP_AVICSCD:
1263 choices[choice_count++] = wxT("Compression Level 0 intraframe-only");
1264 choices[choice_count++] = wxT("Compression Level 1 intraframe-only");
1265 choices[choice_count++] = wxT("Compression Level 2 intraframe-only");
1266 choices[choice_count++] = wxT("Compression Level 3 intraframe-only");
1267 choices[choice_count++] = wxT("Compression Level 4 intraframe-only");
1268 choices[choice_count++] = wxT("Compression Level 5 intraframe-only");
1269 choices[choice_count++] = wxT("Compression Level 6 intraframe-only");
1270 choices[choice_count++] = wxT("Compression Level 7 intraframe-only");
1271 choices[choice_count++] = wxT("Compression Level 8 intraframe-only");
1272 choices[choice_count++] = wxT("Compression Level 9 intraframe-only");
1273 choices[choice_count++] = wxT("Compression Level 1");
1274 choices[choice_count++] = wxT("Compression Level 2");
1275 choices[choice_count++] = wxT("Compression Level 3");
1276 choices[choice_count++] = wxT("Compression Level 4");
1277 choices[choice_count++] = wxT("Compression Level 5");
1278 choices[choice_count++] = wxT("Compression Level 6");
1279 choices[choice_count++] = wxT("Compression Level 7");
1280 choices[choice_count++] = wxT("Compression Level 8");
1281 choices[choice_count++] = wxT("Compression Level 9");
1282 msg = "Choose CSCD compression level: ";
1283 caption = "AVI(CSCD) dump";
1284 is_prefix = true;
1285 cmd = "dump-avi";
1286 has_level = true;
1287 break;
1288 case wxID_DUMP_JMD:
1289 choices[choice_count++] = wxT("Compression Level 0");
1290 choices[choice_count++] = wxT("Compression Level 1");
1291 choices[choice_count++] = wxT("Compression Level 2");
1292 choices[choice_count++] = wxT("Compression Level 3");
1293 choices[choice_count++] = wxT("Compression Level 4");
1294 choices[choice_count++] = wxT("Compression Level 5");
1295 choices[choice_count++] = wxT("Compression Level 6");
1296 choices[choice_count++] = wxT("Compression Level 7");
1297 choices[choice_count++] = wxT("Compression Level 8");
1298 choices[choice_count++] = wxT("Compression Level 9");
1299 msg = "Choose JMD compression level: ";
1300 caption = "JMD dump";
1301 is_prefix = false;
1302 cmd = "dump-jmd";
1303 has_level = true;
1304 break;
1305 case wxID_DUMP_SDMP:
1306 choices[choice_count++] = wxT("Segmented");
1307 choices[choice_count++] = wxT("Single segment");
1308 msg = "Choose SDMP settings: ";
1309 caption = "SDMP dump";
1310 is_prefix = false;
1311 cmd = "dump-sdmpss";
1312 has_level = false;
1313 break;
1315 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, towxstring(msg), towxstring(caption), choice_count,
1316 choices);
1317 if(d->ShowModal() == wxID_CANCEL) {
1318 d->Destroy();
1319 return;
1321 int choice = d->GetSelection();
1322 if(e.GetId() == wxID_DUMP_SDMP && choice == 0) {
1323 cmd = "dump-sdmp";
1324 is_prefix = true;
1326 d->Destroy();
1328 std::string prefix;
1329 wxFileDialog* d2 = new wxFileDialog(this, is_prefix ? wxT("Dump prefix") : wxT("Dump file"), wxT("."));
1330 if(d2->ShowModal() == wxID_OK)
1331 prefix = tostdstring(d2->GetPath());
1332 d2->Destroy();
1334 std::ostringstream str;
1335 str << cmd;
1336 if(has_level)
1337 str << " " << choice;
1338 str << " " << prefix;
1339 exec_command(str.str());
1343 void emulator_main_window::menu_audio_status(wxCommandEvent& e)
1345 exec_command("show-sound-status");
1348 void emulator_main_window::menu_choose_audio_device(wxCommandEvent& e)
1350 if(!audio_devs.count(e.GetId()))
1351 return; //Not supposed to happen.
1352 window::set_sound_device(audio_devs[e.GetId()]);
1355 void emulator_main_window::menu_loadsave(wxCommandEvent& e)
1357 int id = e.GetId();
1358 bool is_save = (id == wxID_SAVE_MOVIE || id == wxID_SAVE_STATE || id == wxID_SAVE_SCREENSHOT);
1359 std::string filename;
1361 wxFileDialog* d = new wxFileDialog(this, is_save ? wxT("Save") : wxT("Load"), wxT("."));
1362 if(d->ShowModal() == wxID_OK)
1363 filename = tostdstring(d->GetPath());
1364 d->Destroy();
1365 if(filename == "")
1366 return;
1368 switch(id) {
1369 case wxID_LOAD_MOVIE:
1370 exec_command("load-movie " + filename);
1371 break;
1372 case wxID_LOAD_STATE:
1373 exec_command("load " + filename);
1374 break;
1375 case wxID_LOAD_STATE_RO:
1376 exec_command("load-readonly " + filename);
1377 break;
1378 case wxID_LOAD_STATE_RW:
1379 exec_command("load-state " + filename);
1380 break;
1381 case wxID_SAVE_MOVIE:
1382 exec_command("save-movie " + filename);
1383 break;
1384 case wxID_SAVE_STATE:
1385 exec_command("save-state " + filename);
1386 break;
1387 case wxID_SAVE_SCREENSHOT:
1388 exec_command("take-screenshot " + filename);
1389 break;
1393 void emulator_main_window::menu_scripting(wxCommandEvent& e)
1395 int id = e.GetId();
1396 bool file = (id == wxID_RUN_LUA || id == wxID_RUN_SCRIPT);
1397 std::string name;
1399 if(file) {
1400 wxFileDialog* d = new wxFileDialog(this, wxT("Select Script"), wxT("."));
1401 if(d->ShowModal() == wxID_OK)
1402 name = tostdstring(d->GetPath());
1403 d->Destroy();
1404 } else {
1405 wxTextEntryDialog* d = new wxTextEntryDialog(this, wxT("Enter Lua statement:"),
1406 wxT("Evaluate Lua"));
1407 if(d->ShowModal() == wxID_OK)
1408 name = tostdstring(d->GetValue());
1409 d->Destroy();
1411 if(name == "")
1412 return;
1414 switch(id) {
1415 case wxID_RUN_SCRIPT:
1416 exec_command("run-script " + name);
1417 break;
1418 case wxID_EVAL_LUA:
1419 exec_command("evaluate-lua " + name);
1420 break;
1421 case wxID_RUN_LUA:
1422 exec_command("run-lua " + name);
1423 break;
1427 sound_listener::sound_listener(emulator_main_window* w)
1429 win = w;
1432 sound_listener::~sound_listener() throw()
1436 void sound_listener::on_sound_unmute(bool unmute) throw()
1438 if(win && win->sound_enable)
1439 win->sound_enable->Check(unmute);
1442 void sound_listener::on_mode_change(bool readonly) throw()
1444 if(win && win->readonly_enable)
1445 win->readonly_enable->Check(readonly);
1448 void sound_listener::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
1450 if(win && win->ahmenu)
1451 win->ahmenu->update(pid, ctrlnum, newstate);
1454 void sound_listener::on_autohold_reconfigure()
1456 if(win && win->ahmenu)
1457 win->ahmenu->reconfigure();
1460 void sound_listener::on_sound_change(const std::string& dev) throw()
1462 int j = wxID_ANY;
1463 for(auto i : audio_devs) {
1464 if(dev == i.second) {
1465 j = i.first;
1466 break;
1469 if(j == wxID_ANY)
1470 return;
1471 audio_devitems[j]->Check();
1474 void boot_emulator(loaded_rom& rom, moviefile& movie)
1476 wx_status_window* status = new wx_status_window();
1477 window2 = status;
1478 status->Show();
1479 std::string windowname = "lsnes-" + lsnes_version + "[" + bsnes_core_version + "]";
1480 main_window = new emulator_main_window(windowname);
1481 struct emulator_boot_state s;
1482 s.rom = &rom;
1483 s.movie = &movie;
1484 emu_cr = new coroutine(emulator_bootup_fn, &s, STACKSIZE);
1485 messages << "Started emulator main coroutine" << std::endl;
1486 //Delete the rom and movie. They aren't needed anymore.
1487 delete &movie;
1488 main_window->Show();
1491 void exec_command(const std::string& cmd)
1493 if(!emu_cr)
1494 return;
1495 pending_command = cmd;
1496 request = REQ_COMMAND;
1497 emu_cr->resume();
1501 void window::poll_inputs() throw(std::bad_alloc)
1503 do {
1504 coroutine::yield();
1505 if(request == REQ_POLL_JOYSTICK)
1506 window::poll_joysticks();
1507 if(request == REQ_KEY_PRESS)
1508 presed_key->set_position(1, keypress_modifiers);
1509 if(request == REQ_KEY_RELEASE)
1510 presed_key->set_position(0, keypress_modifiers);
1511 if(request == REQ_CONTINUE)
1512 break;
1513 if(request == REQ_COMMAND)
1514 command::invokeC(pending_command);
1515 } while(1);
1518 void window::notify_screen_update(bool full) throw()
1520 screen_updated_full = true;
1521 screen_updated = true;
1522 if(main_window && !main_window_dirty) {
1523 main_window_dirty = true;
1524 main_window->request_paint();
1526 if(wx_status_window::ptr)
1527 wx_status_window::ptr->notify_status_change();
1530 void window::set_main_surface(screen& scr) throw()
1532 main_screen = &scr;
1533 screen_updated_full = true;
1534 screen_updated = true;
1537 void window::paused(bool enable) throw()
1539 e_paused = enable;
1540 screen_updated = true;
1543 void window::wait_usec(uint64_t usec) throw(std::bad_alloc)
1545 waiting = true;
1546 wait_until = get_utime() + usec;
1547 poll_inputs();
1550 void window::cancel_wait() throw()
1552 waiting = false;
1555 void window::fatal_error2() throw()
1557 in_modal_dialog = true;
1558 std::string err = "Unknown fatal error occured";
1559 if(window::msgbuf.get_msg_count() > 0)
1560 err = window::msgbuf.get_message(window::msgbuf.get_msg_first() + window::msgbuf.get_msg_count() - 1);
1561 wxMessageDialog* d = new wxMessageDialog(main_window, towxstring(err), wxT("Fatal Error"),
1562 wxOK | wxICON_ERROR);
1563 d->ShowModal();
1564 exit(1);
1567 bool window::modal_message(const std::string& msg, bool confirm) throw(std::bad_alloc)
1569 in_modal_dialog = true;
1570 wxMessageDialog* d;
1571 if(confirm)
1572 d = new wxMessageDialog(main_window, towxstring(msg), wxT("Question"),
1573 wxOK | wxCANCEL | wxICON_QUESTION);
1574 else
1575 d = new wxMessageDialog(main_window, towxstring(msg), wxT("Information"),
1576 wxOK | wxICON_INFORMATION);
1577 auto r = d->ShowModal();
1578 d->Destroy();
1579 in_modal_dialog = false;
1580 if(r == wxID_OK)
1581 return confirm;
1582 return false;
1585 void graphics_init()
1587 init_modifiers_and_keys();
1590 void graphics_quit()
1592 for(auto i : modifier_map)
1593 delete i.second;
1594 for(auto i : keys_allocated)
1595 delete key_map[i.second];
1596 modifier_map.clear();
1597 key_map.clear();
1598 keys_allocated.clear();
1599 keys_held.clear();
1603 const char* graphics_plugin_name = "Wxwidgets graphics plugin";