Wxwidgets: VU meters & volume adjustment window
[lsnes.git] / src / platform / wxwidgets / mainwindow.cpp
blob28fbfc4f9660f3b04d65e80160db2f853d033301
1 #include "lsnes.hpp"
3 #include "core/emucore.hpp"
5 #include "core/audioapi.hpp"
6 #include "core/command.hpp"
7 #include "core/controller.hpp"
8 #include "core/controllerframe.hpp"
9 #include "core/dispatch.hpp"
10 #include "core/framebuffer.hpp"
11 #include "core/framerate.hpp"
12 #include "core/loadlib.hpp"
13 #include "lua/lua.hpp"
14 #include "core/mainloop.hpp"
15 #include "core/memorywatch.hpp"
16 #include "core/misc.hpp"
17 #include "core/moviedata.hpp"
18 #include "core/settings.hpp"
19 #include "core/window.hpp"
20 #include "library/minmax.hpp"
21 #include "library/string.hpp"
22 #include "library/zip.hpp"
24 #include <wx/dnd.h>
26 #include <cmath>
27 #include <vector>
28 #include <string>
30 #include "platform/wxwidgets/menu_dump.hpp"
31 #include "platform/wxwidgets/platform.hpp"
32 #include "platform/wxwidgets/window_mainwindow.hpp"
33 #include "platform/wxwidgets/window_messages.hpp"
34 #include "platform/wxwidgets/window_status.hpp"
36 extern "C"
38 #ifndef UINT64_C
39 #define UINT64_C(val) val##ULL
40 #endif
41 #include <libswscale/swscale.h>
44 enum
46 wxID_PAUSE = wxID_HIGHEST + 1,
47 wxID_FRAMEADVANCE,
48 wxID_SUBFRAMEADVANCE,
49 wxID_NEXTPOLL,
50 wxID_ERESET,
51 wxID_AUDIO_ENABLED,
52 wxID_AUDIODEV_FIRST,
53 wxID_AUDIODEV_LAST = wxID_AUDIODEV_FIRST + 255,
54 wxID_SAVE_STATE,
55 wxID_SAVE_MOVIE,
56 wxID_SAVE_SUBTITLES,
57 wxID_LOAD_STATE,
58 wxID_LOAD_STATE_RO,
59 wxID_LOAD_STATE_RW,
60 wxID_LOAD_STATE_P,
61 wxID_LOAD_MOVIE,
62 wxID_RUN_SCRIPT,
63 wxID_RUN_LUA,
64 wxID_RESET_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 + 1023,
71 wxID_EDIT_MEMORYWATCH,
72 wxID_SAVE_MEMORYWATCH,
73 wxID_LOAD_MEMORYWATCH,
74 wxID_EDIT_SUBTITLES,
75 wxID_EDIT_VSUBTITLES,
76 wxID_DUMP_FIRST,
77 wxID_DUMP_LAST = wxID_DUMP_FIRST + 1023,
78 wxID_REWIND_MOVIE,
79 wxID_MEMORY_SEARCH,
80 wxID_CANCEL_SAVES,
81 wxID_SHOW_STATUS,
82 wxID_SET_SPEED,
83 wxID_SPEED_5,
84 wxID_SPEED_10,
85 wxID_SPEED_17,
86 wxID_SPEED_20,
87 wxID_SPEED_25,
88 wxID_SPEED_33,
89 wxID_SPEED_50,
90 wxID_SPEED_100,
91 wxID_SPEED_150,
92 wxID_SPEED_200,
93 wxID_SPEED_300,
94 wxID_SPEED_500,
95 wxID_SPEED_1000,
96 wxID_SPEED_TURBO,
97 wxID_LOAD_LIBRARY,
98 wxID_SETTINGS,
99 wxID_SETTINGS_HOTKEYS,
100 wxID_RELOAD_ROM_IMAGE,
101 wxID_LOAD_ROM_IMAGE,
102 wxID_NEW_MOVIE,
103 wxID_SHOW_MESSAGES,
104 wxID_DEDICATED_MEMORY_WATCH,
105 wxID_RMOVIE_FIRST,
106 wxID_RMOVIE_LAST = wxID_RMOVIE_FIRST + 16,
107 wxID_RROM_FIRST,
108 wxID_RROM_LAST = wxID_RROM_FIRST + 16,
109 wxID_VUDISPLAY
113 double horizontal_scale_factor = 1.0;
114 double vertical_scale_factor = 1.0;
115 int scaling_flags = SWS_POINT;
116 bool hflip_enabled = false;
117 bool vflip_enabled = false;
118 bool rotate_enabled = false;
120 namespace
122 std::string last_volume = "0dB";
123 std::string last_volume_record = "0dB";
124 std::string last_volume_voice = "0dB";
125 unsigned char* screen_buffer;
126 uint32_t* rotate_buffer;
127 uint32_t old_width;
128 uint32_t old_height;
129 int old_flags = SWS_POINT;
130 bool old_hflip = false;
131 bool old_vflip = false;
132 bool old_rotate = false;
133 bool main_window_dirty;
134 struct thread* emulation_thread;
136 double pick_volume(wxWindow* win, const std::string& title, std::string& last)
138 std::string value;
139 regex_results r;
140 double parsed = 1;
141 value = pick_text(win, title, "Enter volume in absolute units, percentage (%) or dB:",
142 last);
143 if(r = regex("([0-9]*\\.[0-9]+|[0-9]+)", value))
144 parsed = strtod(r[1].c_str(), NULL);
145 else if(r = regex("([0-9]*\\.[0-9]+|[0-9]+)%", value))
146 parsed = strtod(r[1].c_str(), NULL) / 100;
147 else if(r = regex("([+-]?([0-9]*.[0-9]+|[0-9]+))dB", value))
148 parsed = pow(10, strtod(r[1].c_str(), NULL) / 20);
149 else {
150 wxMessageBox(wxT("Invalid volume"), _T("Error"), wxICON_EXCLAMATION | wxOK, win);
151 return -1;
153 last = value;
154 return parsed;
157 void recent_rom_selected(const std::string& file)
159 platform::queue("unpause-emulator");
160 platform::queue("reload-rom " + file);
163 void recent_movie_selected(const std::string& file)
165 platform::queue("load-smart " + file);
168 wxString getname()
170 std::string windowname = "lsnes rr" + lsnes_version + "[" + bsnes_core_version + "]";
171 return towxstring(windowname);
174 struct emu_args
176 struct loaded_rom* rom;
177 struct moviefile* initial;
178 bool load_has_to_succeed;
181 void* emulator_main(void* _args)
183 struct emu_args* args = reinterpret_cast<struct emu_args*>(_args);
184 try {
185 our_rom = args->rom;
186 struct moviefile* movie = args->initial;
187 bool has_to_succeed = args->load_has_to_succeed;
188 platform::flush_command_queue();
189 main_loop(*our_rom, *movie, has_to_succeed);
190 signal_program_exit();
191 } catch(std::bad_alloc& e) {
192 OOM_panic();
193 } catch(std::exception& e) {
194 messages << "FATAL: " << e.what() << std::endl;
195 platform::fatal_error();
197 return NULL;
200 void join_emulator_thread()
202 emulation_thread->join();
205 keygroup mouse_x("mouse_x", "mouse", keygroup::KT_MOUSE);
206 keygroup mouse_y("mouse_y", "mouse", keygroup::KT_MOUSE);
207 keygroup mouse_l("mouse_left", "mouse", keygroup::KT_KEY);
208 keygroup mouse_m("mouse_center", "mouse", keygroup::KT_KEY);
209 keygroup mouse_r("mouse_right", "mouse", keygroup::KT_KEY);
210 keygroup mouse_i("mouse_inwindow", "mouse", keygroup::KT_KEY);
212 void handle_wx_mouse(wxMouseEvent& e)
214 platform::queue(keypress(modifier_set(), mouse_x, e.GetX() / horizontal_scale_factor));
215 platform::queue(keypress(modifier_set(), mouse_y, e.GetY() / vertical_scale_factor));
216 if(e.Entering())
217 platform::queue(keypress(modifier_set(), mouse_i, 1));
218 if(e.Leaving())
219 platform::queue(keypress(modifier_set(), mouse_i, 0));
220 if(e.LeftDown())
221 platform::queue(keypress(modifier_set(), mouse_l, 1));
222 if(e.LeftUp())
223 platform::queue(keypress(modifier_set(), mouse_l, 0));
224 if(e.MiddleDown())
225 platform::queue(keypress(modifier_set(), mouse_m, 1));
226 if(e.MiddleUp())
227 platform::queue(keypress(modifier_set(), mouse_m, 0));
228 if(e.RightDown())
229 platform::queue(keypress(modifier_set(), mouse_r, 1));
230 if(e.RightUp())
231 platform::queue(keypress(modifier_set(), mouse_r, 0));
234 bool is_readonly_mode()
236 bool ret;
237 runemufn([&ret]() { ret = movb.get_movie().readonly_mode(); });
238 return ret;
241 bool UI_get_autohold(unsigned pid, unsigned idx)
243 bool ret;
244 runemufn([&ret, pid, idx]() { ret = controls.autohold(pid, idx); });
245 return ret;
248 void UI_change_autohold(unsigned pid, unsigned idx, bool newstate)
250 runemufn([pid, idx, newstate]() { controls.autohold(pid, idx, newstate); });
253 int UI_controller_index_by_logical(unsigned lid)
255 int ret;
256 runemufn([&ret, lid]() { ret = controls.lcid_to_pcid(lid); });
257 return ret;
260 int UI_button_id(unsigned pcid, unsigned lidx)
262 int ret;
263 runemufn([&ret, pcid, lidx]() { ret = controls.button_id(pcid, lidx); });
264 return ret;
267 void set_speed(double target)
269 std::string v = (stringfmt() << target).str();
270 if(target < 0)
271 setting::set("targetfps", "infinite");
272 else
273 setting::set("targetfps", v);
276 class controller_autohold_menu : public wxMenu
278 public:
279 controller_autohold_menu(unsigned lid);
280 void change_type();
281 bool is_dummy();
282 void on_select(wxCommandEvent& e);
283 void update(unsigned pid, unsigned ctrlnum, bool newstate);
284 private:
285 unsigned our_lid;
286 int our_pid;
287 std::vector<wxMenuItem*> entries;
288 unsigned enabled_entries;
289 std::map<unsigned, int> pidxs;
290 std::vector<bool> autoholds;
293 class autohold_menu : public wxMenu
295 public:
296 autohold_menu(wxwin_mainwindow* win);
297 void reconfigure();
298 void on_select(wxCommandEvent& e);
299 void update(unsigned pid, unsigned ctrlnum, bool newstate);
300 private:
301 std::vector<controller_autohold_menu*> menus;
302 std::vector<wxMenuItem*> entries;
305 class sound_select_menu : public wxMenu
307 public:
308 sound_select_menu(wxwin_mainwindow* win);
309 void update(const std::string& dev);
310 void on_select(wxCommandEvent& e);
311 private:
312 std::map<std::string, wxMenuItem*> items;
313 std::map<int, std::string> devices;
316 class sound_select_menu;
318 class broadcast_listener : public information_dispatch
320 public:
321 broadcast_listener(wxwin_mainwindow* win);
322 void set_sound_select(sound_select_menu* sdev);
323 void set_autohold_menu(autohold_menu* ah);
324 void on_sound_unmute(bool unmute) throw();
325 void on_sound_change(const std::string& dev) throw();
326 void on_mode_change(bool readonly) throw();
327 void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
328 void on_autohold_reconfigure();
329 private:
330 wxwin_mainwindow* mainw;
331 sound_select_menu* sounddev;
332 autohold_menu* ahmenu;
335 controller_autohold_menu::controller_autohold_menu(unsigned lid)
337 auto limits = get_core_logical_controller_limits();
338 entries.resize(limits.second);
339 modal_pause_holder hld;
340 our_lid = lid;
341 for(unsigned i = 0; i < limits.second; i++) {
342 int id = wxID_AUTOHOLD_FIRST + limits.second * lid + i;
343 entries[i] = AppendCheckItem(id, towxstring(get_logical_button_name(i)));
345 change_type();
348 void controller_autohold_menu::change_type()
350 enabled_entries = 0;
351 our_pid = controls.lcid_to_pcid(our_lid);
352 auto limits = get_core_logical_controller_limits();
353 this->autoholds.resize(limits.second);
354 //We have asserted modal pause.
355 for(unsigned i = 0; i < limits.second; i++) {
356 this->pidxs[i] = -1;
357 if(this->our_pid >= 0)
358 this->pidxs[i] = controls.button_id(this->our_pid, i);
359 if(this->pidxs[i] >= 0)
360 this->autoholds[i] = controls.autohold(this->our_pid, this->pidxs[i]);
361 else
362 this->autoholds[i] = false;
364 for(auto i : pidxs) {
365 if(i.second >= 0) {
366 entries[i.first]->Check(autoholds[i.first]);
367 entries[i.first]->Enable();
368 enabled_entries++;
369 } else {
370 entries[i.first]->Check(false);
371 entries[i.first]->Enable(false);
376 bool controller_autohold_menu::is_dummy()
378 return !enabled_entries;
381 void controller_autohold_menu::on_select(wxCommandEvent& e)
383 auto limits = get_core_logical_controller_limits();
384 int x = e.GetId();
385 if(x < wxID_AUTOHOLD_FIRST + our_lid * limits.second || x >= wxID_AUTOHOLD_FIRST *
386 (our_lid + 1) * limits.second) {
387 return;
389 unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % limits.second;
390 modal_pause_holder hld;
391 int pid = controls.lcid_to_pcid(our_lid);
392 if(pid < 0 || !entries[lidx])
393 return;
394 int pidx = controls.button_id(pid, lidx);
395 if(pidx < 0)
396 return;
397 //Autohold change on pid=pid, ctrlindx=idx, state
398 bool newstate = entries[lidx]->IsChecked();
399 UI_change_autohold(pid, pidx, newstate);
402 void controller_autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
404 modal_pause_holder hld;
405 if(our_pid < 0 || static_cast<unsigned>(pid) != our_pid)
406 return;
407 auto limits = get_core_logical_controller_limits();
408 for(unsigned i = 0; i < limits.second; i++) {
409 if(pidxs[i] < 0 || static_cast<unsigned>(pidxs[i]) != ctrlnum)
410 continue;
411 entries[i]->Check(newstate);
416 autohold_menu::autohold_menu(wxwin_mainwindow* win)
418 auto limits = get_core_logical_controller_limits();
419 entries.resize(limits.first);
420 menus.resize(limits.first);
421 for(unsigned i = 0; i < limits.first; i++) {
422 std::ostringstream str;
423 str << "Controller #&" << (i + 1);
424 menus[i] = new controller_autohold_menu(i);
425 entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
426 entries[i]->Enable(!menus[i]->is_dummy());
428 win->Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED,
429 wxCommandEventHandler(autohold_menu::on_select), NULL, this);
430 reconfigure();
433 void autohold_menu::reconfigure()
435 modal_pause_holder hld;
436 auto limits = get_core_logical_controller_limits();
437 for(unsigned i = 0; i < limits.first; i++) {
438 menus[i]->change_type();
439 entries[i]->Enable(!menus[i]->is_dummy());
443 void autohold_menu::on_select(wxCommandEvent& e)
445 auto limits = get_core_logical_controller_limits();
446 for(unsigned i = 0; i < limits.first; i++)
447 menus[i]->on_select(e);
450 void autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
452 auto limits = get_core_logical_controller_limits();
453 for(unsigned i = 0; i < limits.first; i++)
454 menus[i]->update(pid, ctrlnum, newstate);
457 sound_select_menu::sound_select_menu(wxwin_mainwindow* win)
459 std::string curdev = audioapi_driver_get_device();
460 int j = wxID_AUDIODEV_FIRST;
461 for(auto i : audioapi_driver_get_devices()) {
462 items[i.first] = AppendRadioItem(j, towxstring(i.first + "(" + i.second + ")"));
463 devices[j] = i.first;
464 if(i.first == curdev)
465 items[i.first]->Check();
466 win->Connect(j, wxEVT_COMMAND_MENU_SELECTED,
467 wxCommandEventHandler(sound_select_menu::on_select), NULL, this);
468 j++;
472 void sound_select_menu::update(const std::string& dev)
474 items[dev]->Check();
477 void sound_select_menu::on_select(wxCommandEvent& e)
479 std::string devname = devices[e.GetId()];
480 if(devname != "")
481 runemufn([devname]() { platform::set_sound_device(devname); });
484 broadcast_listener::broadcast_listener(wxwin_mainwindow* win)
485 : information_dispatch("wxwidgets-broadcast-listener")
487 mainw = win;
490 void broadcast_listener::set_sound_select(sound_select_menu* sdev)
492 sounddev = sdev;
495 void broadcast_listener::set_autohold_menu(autohold_menu* ah)
497 ahmenu = ah;
500 void broadcast_listener::on_sound_unmute(bool unmute) throw()
502 runuifun([this, unmute]() { this->mainw->menu_check(wxID_AUDIO_ENABLED, unmute); });
505 void broadcast_listener::on_sound_change(const std::string& dev) throw()
507 runuifun([this, dev]() { if(this->sounddev) this->sounddev->update(dev); });
510 void broadcast_listener::on_mode_change(bool readonly) throw()
512 runuifun([this, readonly]() { this->mainw->menu_check(wxID_READONLY_MODE, readonly); });
515 void broadcast_listener::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
517 runuifun([this, pid, ctrlnum, newstate]() { this->ahmenu->update(pid, ctrlnum, newstate); });
520 void broadcast_listener::on_autohold_reconfigure()
522 runuifun([this]() { this->ahmenu->reconfigure(); });
525 path_setting moviepath_setting("moviepath");
526 path_setting rompath_setting("rompath");
528 std::string movie_path()
530 return setting::get("moviepath");
533 std::string rom_path()
535 return setting::get("rompath");
538 bool is_lsnes_movie(const std::string& filename)
540 try {
541 zip_reader r(filename);
542 std::istream& s = r["systemid"];
543 std::string s2;
544 std::getline(s, s2);
545 delete &s;
546 istrip_CR(s2);
547 return (s2 == "lsnes-rr1");
548 } catch(...) {
549 return false;
553 class loadfile : public wxFileDropTarget
555 public:
556 loadfile(wxwin_mainwindow* win) : pwin(win) {};
557 bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
559 bool ret = false;
560 if(filenames.Count() == 2) {
561 if(is_lsnes_movie(tostdstring(filenames[0])) &&
562 !is_lsnes_movie(tostdstring(filenames[1]))) {
563 platform::queue("unpause-emulator");
564 platform::queue("reload-rom " + tostdstring(filenames[1]));
565 platform::queue("load-smart " + tostdstring(filenames[0]));
566 ret = true;
568 if(!is_lsnes_movie(tostdstring(filenames[0])) &&
569 is_lsnes_movie(tostdstring(filenames[1]))) {
570 platform::queue("unpause-emulator");
571 platform::queue("reload-rom " + tostdstring(filenames[0]));
572 platform::queue("load-smart " + tostdstring(filenames[1]));
573 ret = true;
576 if(filenames.Count() == 1) {
577 if(is_lsnes_movie(tostdstring(filenames[0]))) {
578 platform::queue("load-smart " + tostdstring(filenames[0]));
579 pwin->recent_movies->add(tostdstring(filenames[0]));
580 ret = true;
581 } else {
582 platform::queue("unpause-emulator");
583 platform::queue("reload-rom " + tostdstring(filenames[0]));
584 pwin->recent_roms->add(tostdstring(filenames[0]));
585 ret = true;
588 return ret;
590 wxwin_mainwindow* pwin;
594 void boot_emulator(loaded_rom& rom, moviefile& movie)
596 try {
597 struct emu_args* a = new emu_args;
598 a->rom = &rom;
599 a->initial = &movie;
600 a->load_has_to_succeed = false;
601 modal_pause_holder hld;
602 emulation_thread = &thread::create(emulator_main, a);
603 main_window = new wxwin_mainwindow();
604 main_window->Show();
605 } catch(std::bad_alloc& e) {
606 OOM_panic();
610 wxwin_mainwindow::panel::panel(wxWindow* win)
611 : wxPanel(win, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS)
613 this->Connect(wxEVT_PAINT, wxPaintEventHandler(panel::on_paint), NULL, this);
614 this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(panel::on_erase), NULL, this);
615 this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(panel::on_keyboard_down), NULL, this);
616 this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(panel::on_keyboard_up), NULL, this);
617 this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
618 this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
619 this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
620 this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
621 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
622 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
623 this->Connect(wxEVT_MOTION, wxMouseEventHandler(panel::on_mouse), NULL, this);
624 this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(panel::on_mouse), NULL, this);
625 this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(panel::on_mouse), NULL, this);
626 SetMinSize(wxSize(512, 448));
629 void wxwin_mainwindow::menu_start(wxString name)
631 while(!upper.empty())
632 upper.pop();
633 current_menu = new wxMenu();
634 menubar->Append(current_menu, name);
637 void wxwin_mainwindow::menu_special(wxString name, wxMenu* menu)
639 while(!upper.empty())
640 upper.pop();
641 menubar->Append(menu, name);
642 current_menu = NULL;
645 void wxwin_mainwindow::menu_special_sub(wxString name, wxMenu* menu)
647 current_menu->AppendSubMenu(menu, name);
650 void wxwin_mainwindow::menu_entry(int id, wxString name)
652 current_menu->Append(id, name);
653 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
654 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
657 void wxwin_mainwindow::menu_entry_check(int id, wxString name)
659 checkitems[id] = current_menu->AppendCheckItem(id, name);
660 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
661 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
664 void wxwin_mainwindow::menu_start_sub(wxString name)
666 wxMenu* old = current_menu;
667 upper.push(current_menu);
668 current_menu = new wxMenu();
669 old->AppendSubMenu(current_menu, name);
672 void wxwin_mainwindow::menu_end_sub()
674 current_menu = upper.top();
675 upper.pop();
678 bool wxwin_mainwindow::menu_ischecked(int id)
680 if(checkitems.count(id))
681 return checkitems[id]->IsChecked();
682 else
683 return false;
686 void wxwin_mainwindow::menu_check(int id, bool newstate)
688 if(checkitems.count(id))
689 return checkitems[id]->Check(newstate);
690 else
691 return;
694 void wxwin_mainwindow::menu_separator()
696 current_menu->AppendSeparator();
699 void wxwin_mainwindow::panel::request_paint()
701 Refresh();
704 void wxwin_mainwindow::panel::on_paint(wxPaintEvent& e)
706 render_framebuffer();
707 static struct SwsContext* ctx;
708 uint8_t* srcp[1];
709 int srcs[1];
710 uint8_t* dstp[1];
711 int dsts[1];
712 wxPaintDC dc(this);
713 uint32_t tw, th;
714 bool aux = hflip_enabled || vflip_enabled || rotate_enabled;
715 if(rotate_enabled) {
716 tw = main_screen.get_height() * horizontal_scale_factor + 0.5;
717 th = main_screen.get_width() * vertical_scale_factor + 0.5;
718 } else {
719 tw = main_screen.get_width() * horizontal_scale_factor + 0.5;
720 th = main_screen.get_height() * vertical_scale_factor + 0.5;
722 if(!tw || !th) {
723 main_window_dirty = false;
724 return;
726 if(!screen_buffer || tw != old_width || th != old_height || scaling_flags != old_flags ||
727 hflip_enabled != old_hflip || vflip_enabled != old_vflip || rotate_enabled != old_rotate) {
728 if(screen_buffer)
729 delete[] screen_buffer;
730 if(rotate_buffer)
731 delete[] rotate_buffer;
732 old_height = th;
733 old_width = tw;
734 old_flags = scaling_flags;
735 old_hflip = hflip_enabled;
736 old_vflip = vflip_enabled;
737 old_rotate = rotate_enabled;
738 uint32_t w = main_screen.get_width();
739 uint32_t h = main_screen.get_height();
740 if(w && h)
741 ctx = sws_getCachedContext(ctx, rotate_enabled ? h : w, rotate_enabled ? w : h, PIX_FMT_RGBA,
742 tw, th, PIX_FMT_BGR24, scaling_flags, NULL, NULL, NULL);
743 tw = max(tw, static_cast<uint32_t>(128));
744 th = max(th, static_cast<uint32_t>(112));
745 screen_buffer = new unsigned char[tw * th * 3];
746 if(aux)
747 rotate_buffer = new uint32_t[main_screen.get_width() * main_screen.get_height()];
748 SetMinSize(wxSize(tw, th));
749 signal_resize_needed();
751 if(aux) {
752 //Hflip, Vflip or rotate active.
753 size_t width = main_screen.get_width();
754 size_t height = main_screen.get_height();
755 size_t width1 = width - 1;
756 size_t height1 = height - 1;
757 size_t stride = main_screen.rowptr(1) - main_screen.rowptr(0);
758 uint32_t* pixels = main_screen.rowptr(0);
759 if(rotate_enabled) {
760 for(unsigned y = 0; y < height; y++) {
761 uint32_t* pixels2 = pixels + (vflip_enabled ? (height1 - y) : y) * stride;
762 uint32_t* dpixels = rotate_buffer + (height1 - y);
763 if(hflip_enabled)
764 for(unsigned x = 0; x < width; x++)
765 dpixels[x * height] = pixels2[width1 - x];
766 else
767 for(unsigned x = 0; x < width; x++)
768 dpixels[x * height] = pixels2[x];
770 } else {
771 for(unsigned y = 0; y < height; y++) {
772 uint32_t* pixels2 = pixels + (vflip_enabled ? (height1 - y) : y) * stride;
773 uint32_t* dpixels = rotate_buffer + y * width;
774 if(hflip_enabled)
775 for(unsigned x = 0; x < width; x++)
776 dpixels[x] = pixels2[width1 - x];
777 else
778 for(unsigned x = 0; x < width; x++)
779 dpixels[x] = pixels2[x];
783 srcs[0] = 4 * (rotate_enabled ? main_screen.get_height() : main_screen.get_width());
784 dsts[0] = 3 * tw;
785 srcp[0] = reinterpret_cast<unsigned char*>(aux ? rotate_buffer : main_screen.rowptr(0));
786 dstp[0] = screen_buffer;
787 memset(screen_buffer, 0, tw * th * 3);
788 if(main_screen.get_width() && main_screen.get_height())
789 sws_scale(ctx, srcp, srcs, 0, rotate_enabled ? main_screen.get_width() : main_screen.get_height(),
790 dstp, dsts);
791 wxBitmap bmp(wxImage(tw, th, screen_buffer, true));
792 dc.DrawBitmap(bmp, 0, 0, false);
793 main_window_dirty = false;
796 void wxwin_mainwindow::panel::on_erase(wxEraseEvent& e)
798 //Blank.
801 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent& e)
803 handle_wx_keyboard(e, true);
806 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent& e)
808 handle_wx_keyboard(e, false);
811 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent& e)
813 handle_wx_mouse(e);
816 wxwin_mainwindow::wxwin_mainwindow()
817 : wxFrame(NULL, wxID_ANY, getname(), wxDefaultPosition, wxSize(-1, -1),
818 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
820 broadcast_listener* blistener = new broadcast_listener(this);
821 Centre();
822 mwindow = NULL;
823 toplevel = new wxFlexGridSizer(1, 2, 0, 0);
824 toplevel->Add(gpanel = new panel(this), 1, wxGROW);
825 toplevel->Add(spanel = new wxwin_status::panel(this, gpanel, 20), 1, wxGROW);
826 spanel_shown = true;
827 toplevel->SetSizeHints(this);
828 SetSizer(toplevel);
829 Fit();
830 gpanel->SetFocus();
831 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_mainwindow::on_close));
832 menubar = new wxMenuBar;
833 SetMenuBar(menubar);
835 menu_start(wxT("File"));
836 menu_start_sub(wxT("New"));
837 menu_entry(wxID_NEW_MOVIE, wxT("Movie..."));
838 menu_end_sub();
839 menu_start_sub(wxT("Load"));
840 menu_entry(wxID_LOAD_STATE, wxT("State..."));
841 menu_entry(wxID_LOAD_STATE_RO, wxT("State (readonly)..."));
842 menu_entry(wxID_LOAD_STATE_RW, wxT("State (read-write)..."));
843 menu_entry(wxID_LOAD_STATE_P, wxT("State (preserve input)..."));
844 menu_entry(wxID_LOAD_MOVIE, wxT("Movie..."));
845 if(load_library_supported) {
846 menu_separator();
847 menu_entry(wxID_LOAD_LIBRARY, towxstring(std::string("Load ") + library_is_called));
849 menu_separator();
850 menu_entry(wxID_RELOAD_ROM_IMAGE, wxT("Reload ROM"));
851 menu_entry(wxID_LOAD_ROM_IMAGE, wxT("ROM..."));
852 menu_separator();
853 menu_special_sub(wxT("Recent ROMs"), recent_roms = new recent_menu(this, wxID_RROM_FIRST, wxID_RROM_LAST,
854 get_config_path() + "/recent-roms.txt", recent_rom_selected));
855 menu_special_sub(wxT("Recent Movies"), recent_movies = new recent_menu(this, wxID_RMOVIE_FIRST,
856 wxID_RMOVIE_LAST, get_config_path() + "/recent-movies.txt", recent_movie_selected));
857 menu_end_sub();
858 menu_start_sub(wxT("Save"));
859 menu_entry(wxID_SAVE_STATE, wxT("State..."));
860 menu_entry(wxID_SAVE_MOVIE, wxT("Movie..."));
861 menu_entry(wxID_SAVE_SCREENSHOT, wxT("Screenshot..."));
862 menu_entry(wxID_SAVE_SUBTITLES, wxT("Subtitles..."));
863 menu_entry(wxID_CANCEL_SAVES, wxT("Cancel pending saves"));
864 menu_end_sub();
865 menu_separator();
866 menu_entry(wxID_EXIT, wxT("Quit"));
868 menu_start(wxT("System"));
869 menu_entry(wxID_PAUSE, wxT("Pause/Unpause"));
870 menu_entry(wxID_FRAMEADVANCE, wxT("Step frame"));
871 menu_entry(wxID_SUBFRAMEADVANCE, wxT("Step subframe"));
872 menu_entry(wxID_NEXTPOLL, wxT("Step poll"));
873 menu_entry(wxID_ERESET, wxT("Reset"));
875 menu_start(wxT("Movie"));
876 menu_entry_check(wxID_READONLY_MODE, wxT("Readonly mode"));
877 menu_check(wxID_READONLY_MODE, is_readonly_mode());
878 menu_entry(wxID_EDIT_AUTHORS, wxT("Edit game name && authors..."));
879 menu_entry(wxID_EDIT_SUBTITLES, wxT("Edit subtitles..."));
880 #ifdef WITH_OPUS_CODEC
881 menu_entry(wxID_EDIT_VSUBTITLES, wxT("Edit commantary track..."));
882 #endif
883 menu_separator();
884 menu_entry(wxID_REWIND_MOVIE, wxT("Rewind to start"));
886 //Autohold menu: (ACOS)
887 menu_special(wxT("Autohold"), reinterpret_cast<autohold_menu*>(ahmenu = new autohold_menu(this)));
888 blistener->set_autohold_menu(reinterpret_cast<autohold_menu*>(ahmenu));
890 menu_start(wxT("Speed"));
891 menu_entry(wxID_SPEED_5, wxT("1/20x"));
892 menu_entry(wxID_SPEED_10, wxT("1/10x"));
893 menu_entry(wxID_SPEED_17, wxT("1/6x"));
894 menu_entry(wxID_SPEED_20, wxT("1/5x"));
895 menu_entry(wxID_SPEED_25, wxT("1/4x"));
896 menu_entry(wxID_SPEED_33, wxT("1/3x"));
897 menu_entry(wxID_SPEED_50, wxT("1/2x"));
898 menu_entry(wxID_SPEED_100, wxT("1x"));
899 menu_entry(wxID_SPEED_150, wxT("1.5x"));
900 menu_entry(wxID_SPEED_200, wxT("2x"));
901 menu_entry(wxID_SPEED_300, wxT("3x"));
902 menu_entry(wxID_SPEED_500, wxT("5x"));
903 menu_entry(wxID_SPEED_1000, wxT("10x"));
904 menu_entry(wxID_SPEED_TURBO, wxT("Turbo"));
905 menu_entry(wxID_SET_SPEED, wxT("Set..."));
907 menu_start(wxT("Tools"));
908 menu_entry(wxID_RUN_SCRIPT, wxT("Run batch file..."));
909 if(lua_supported) {
910 menu_separator();
911 menu_entry(wxID_EVAL_LUA, wxT("Evaluate Lua statement..."));
912 menu_entry(wxID_RUN_LUA, wxT("Run Lua script..."));
913 menu_separator();
914 menu_entry(wxID_RESET_LUA, wxT("Reset Lua VM"));
916 menu_separator();
917 menu_entry(wxID_EDIT_MEMORYWATCH, wxT("Edit memory watch..."));
918 menu_separator();
919 menu_entry(wxID_LOAD_MEMORYWATCH, wxT("Load memory watch..."));
920 menu_entry(wxID_SAVE_MEMORYWATCH, wxT("Save memory watch..."));
921 menu_separator();
922 menu_entry(wxID_MEMORY_SEARCH, wxT("Memory Search..."));
923 menu_separator();
924 menu_special_sub(wxT("Video Capture"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
925 wxID_DUMP_FIRST, wxID_DUMP_LAST)));
927 menu_start(wxT("Configure"));
928 menu_entry_check(wxID_SHOW_STATUS, wxT("Show status panel"));
929 menu_check(wxID_SHOW_STATUS, true);
930 menu_entry_check(wxID_DEDICATED_MEMORY_WATCH, wxT("Dedicated memory watch"));
931 menu_entry(wxID_SHOW_MESSAGES, wxT("Show messages"));
932 menu_entry(wxID_SETTINGS, wxT("Configure emulator..."));
933 menu_entry(wxID_SETTINGS_HOTKEYS, wxT("Configure hotkeys..."));
934 if(audioapi_driver_initialized()) {
935 menu_separator();
936 menu_entry_check(wxID_AUDIO_ENABLED, wxT("Sounds enabled"));
937 menu_entry(wxID_VUDISPLAY, wxT("VU display / volume controls"));
938 menu_check(wxID_AUDIO_ENABLED, platform::is_sound_enabled());
939 menu_special_sub(wxT("Select sound device"), reinterpret_cast<sound_select_menu*>(sounddev =
940 new sound_select_menu(this)));
941 blistener->set_sound_select(reinterpret_cast<sound_select_menu*>(sounddev));
944 menu_start(wxT("Help"));
945 menu_entry(wxID_ABOUT, wxT("About..."));
947 gpanel->SetDropTarget(new loadfile(this));
948 spanel->SetDropTarget(new loadfile(this));
951 void wxwin_mainwindow::request_paint()
953 gpanel->Refresh();
956 void wxwin_mainwindow::on_close(wxCloseEvent& e)
958 //Veto it for now, latter things will delete it.
959 e.Veto();
960 platform::queue("quit-emulator");
963 void wxwin_mainwindow::notify_update() throw()
965 if(!main_window_dirty) {
966 main_window_dirty = true;
967 gpanel->Refresh();
971 void wxwin_mainwindow::notify_resized() throw()
973 toplevel->Layout();
974 toplevel->SetSizeHints(this);
975 Fit();
978 void wxwin_mainwindow::notify_update_status() throw()
980 if(!spanel->dirty) {
981 spanel->dirty = true;
982 spanel->Refresh();
984 if(mwindow)
985 mwindow->notify_update();
988 void wxwin_mainwindow::notify_exit() throw()
990 wxwidgets_exiting = true;
991 join_emulator_thread();
992 Destroy();
995 #define NEW_KEYBINDING "A new binding..."
996 #define NEW_ALIAS "A new alias..."
997 #define NEW_WATCH "A new watch..."
999 void wxwin_mainwindow::handle_menu_click(wxCommandEvent& e)
1001 try {
1002 handle_menu_click_cancelable(e);
1003 } catch(canceled_exception& e) {
1004 //Ignore.
1005 } catch(std::bad_alloc& e) {
1006 OOM_panic();
1007 } catch(std::exception& e) {
1008 show_message_ok(this, "Error in menu handler", e.what(), wxICON_EXCLAMATION);
1012 void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
1014 std::string filename;
1015 bool s;
1016 double parsed;
1017 switch(e.GetId()) {
1018 case wxID_FRAMEADVANCE:
1019 platform::queue("+advance-frame");
1020 platform::queue("-advance-frame");
1021 return;
1022 case wxID_SUBFRAMEADVANCE:
1023 platform::queue("+advance-poll");
1024 platform::queue("-advance-poll");
1025 return;
1026 case wxID_NEXTPOLL:
1027 platform::queue("advance-skiplag");
1028 return;
1029 case wxID_PAUSE:
1030 platform::queue("pause-emulator");
1031 return;
1032 case wxID_ERESET:
1033 platform::queue("reset");
1034 return;
1035 case wxID_EXIT:
1036 platform::queue("quit-emulator");
1037 return;
1038 case wxID_AUDIO_ENABLED:
1039 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED));
1040 return;
1041 case wxID_CANCEL_SAVES:
1042 platform::queue("cancel-saves");
1043 return;
1044 case wxID_LOAD_MOVIE:
1045 filename = pick_file(this, "Load Movie", movie_path(), false);
1046 recent_movies->add(filename);
1047 platform::queue("load-movie " + filename);
1048 return;
1049 case wxID_LOAD_STATE:
1050 filename = pick_file(this, "Load State", movie_path(), false);
1051 recent_movies->add(filename);
1052 platform::queue("load " + filename);
1053 return;
1054 case wxID_LOAD_STATE_RO:
1055 filename = pick_file(this, "Load State (Read-Only)", movie_path(), false);
1056 recent_movies->add(filename);
1057 platform::queue("load-readonly " + filename);
1058 return;
1059 case wxID_LOAD_STATE_RW:
1060 filename = pick_file(this, "Load State (Read-Write)", movie_path(), false);
1061 recent_movies->add(filename);
1062 platform::queue("load-state " + filename);
1063 return;
1064 case wxID_LOAD_STATE_P:
1065 filename = pick_file(this, "Load State (Preserve)", movie_path(), false);
1066 recent_movies->add(filename);
1067 platform::queue("load-preserve " + filename);
1068 return;
1069 case wxID_REWIND_MOVIE:
1070 platform::queue("rewind-movie");
1071 return;
1072 case wxID_SAVE_MOVIE:
1073 filename = pick_file(this, "Save Movie", movie_path(), true);
1074 recent_movies->add(filename);
1075 platform::queue("save-movie " + filename);
1076 return;
1077 case wxID_SAVE_SUBTITLES:
1078 platform::queue("save-subtitle " + pick_file(this, "Save Subtitle (.sub)", movie_path(), true));
1079 return;
1080 case wxID_SAVE_STATE:
1081 filename = pick_file(this, "Save State", movie_path(), true);
1082 recent_movies->add(filename);
1083 platform::queue("save-state " + filename);
1084 return;
1085 case wxID_SAVE_SCREENSHOT:
1086 platform::queue("take-screenshot " + pick_file(this, "Save Screenshot", movie_path(), true));
1087 return;
1088 case wxID_RUN_SCRIPT:
1089 platform::queue("run-script " + pick_file_member(this, "Select Script", "."));
1090 return;
1091 case wxID_RUN_LUA:
1092 platform::queue("run-lua " + pick_file(this, "Select Lua Script", ".", false));
1093 return;
1094 case wxID_RESET_LUA:
1095 platform::queue("reset-lua");
1096 return;
1097 case wxID_EVAL_LUA:
1098 platform::queue("evaluate-lua " + pick_text(this, "Evaluate Lua", "Enter Lua Statement:"));
1099 return;
1100 case wxID_READONLY_MODE:
1101 s = menu_ischecked(wxID_READONLY_MODE);
1102 runemufn([s]() {
1103 movb.get_movie().readonly_mode(s);
1104 if(!s)
1105 lua_callback_do_readwrite();
1106 update_movie_state();
1108 return;
1109 case wxID_EDIT_AUTHORS:
1110 wxeditor_authors_display(this);
1111 return;
1112 case wxID_EDIT_SUBTITLES:
1113 wxeditor_subtitles_display(this);
1114 return;
1115 #ifdef WITH_OPUS_CODEC
1116 case wxID_EDIT_VSUBTITLES:
1117 show_wxeditor_voicesub(this);
1118 return;
1119 #endif
1120 case wxID_EDIT_MEMORYWATCH:
1121 wxeditor_memorywatch_display(this);
1122 return;
1123 case wxID_SAVE_MEMORYWATCH: {
1124 modal_pause_holder hld;
1125 std::set<std::string> old_watches;
1126 runemufn([&old_watches]() { old_watches = get_watches(); });
1127 std::string filename = pick_file(this, "Save watches to file", ".", true);
1128 std::ofstream out(filename.c_str());
1129 for(auto i : old_watches) {
1130 std::string val;
1131 runemufn([i, &val]() { val = get_watchexpr_for(i); });
1132 out << i << std::endl << val << std::endl;
1134 out.close();
1135 return;
1137 case wxID_LOAD_MEMORYWATCH: {
1138 modal_pause_holder hld;
1139 std::set<std::string> old_watches;
1140 runemufn([&old_watches]() { old_watches = get_watches(); });
1141 std::map<std::string, std::string> new_watches;
1142 std::string filename = pick_file_member(this, "Choose memory watch file", ".");
1144 try {
1145 std::istream& in = open_file_relative(filename, "");
1146 while(in) {
1147 std::string wname;
1148 std::string wexpr;
1149 std::getline(in, wname);
1150 std::getline(in, wexpr);
1151 new_watches[strip_CR(wname)] = strip_CR(wexpr);
1153 delete &in;
1154 } catch(std::exception& e) {
1155 show_message_ok(this, "Error", std::string("Can't load memory watch: ") + e.what(),
1156 wxICON_EXCLAMATION);
1157 return;
1160 runemufn([&new_watches, &old_watches]() {
1161 for(auto i : new_watches)
1162 set_watchexpr_for(i.first, i.second);
1163 for(auto i : old_watches)
1164 if(!new_watches.count(i))
1165 set_watchexpr_for(i, "");
1167 return;
1169 case wxID_MEMORY_SEARCH:
1170 wxwindow_memorysearch_display();
1171 return;
1172 case wxID_ABOUT: {
1173 std::ostringstream str;
1174 str << "Version: lsnes rr" << lsnes_version << std::endl;
1175 str << "Revision: " << lsnes_git_revision << std::endl;
1176 str << "Core: " << bsnes_core_version << std::endl;
1177 wxMessageBox(towxstring(str.str()), _T("About"), wxICON_INFORMATION | wxOK, this);
1178 return;
1180 case wxID_SHOW_STATUS: {
1181 bool newstate = menu_ischecked(wxID_SHOW_STATUS);
1182 if(newstate)
1183 spanel->Show();
1184 if(newstate && !spanel_shown)
1185 toplevel->Add(spanel, 1, wxGROW);
1186 else if(!newstate && spanel_shown)
1187 toplevel->Detach(spanel);
1188 if(!newstate)
1189 spanel->Hide();
1190 spanel_shown = newstate;
1191 toplevel->Layout();
1192 toplevel->SetSizeHints(this);
1193 Fit();
1194 return;
1196 case wxID_DEDICATED_MEMORY_WATCH: {
1197 bool newstate = menu_ischecked(wxID_DEDICATED_MEMORY_WATCH);
1198 if(newstate && !mwindow) {
1199 mwindow = new wxwin_status(-1, "Memory Watch");
1200 spanel->set_watch_flag(1);
1201 mwindow->Show();
1202 } else if(!newstate && mwindow) {
1203 mwindow->Destroy();
1204 mwindow = NULL;
1205 spanel->set_watch_flag(0);
1207 return;
1209 case wxID_SET_SPEED: {
1210 bool bad = false;
1211 std::string value = setting::is_set("targetfps") ? setting::get("targetfps") : "";
1212 value = pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value);
1213 try {
1214 setting::set("targetfps", value);
1215 } catch(...) {
1216 wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this);
1218 return;
1220 case wxID_SPEED_5:
1221 set_speed(5);
1222 break;
1223 case wxID_SPEED_10:
1224 set_speed(10);
1225 break;
1226 case wxID_SPEED_17:
1227 set_speed(16.66666666666);
1228 break;
1229 case wxID_SPEED_20:
1230 set_speed(20);
1231 break;
1232 case wxID_SPEED_25:
1233 set_speed(25);
1234 break;
1235 case wxID_SPEED_33:
1236 set_speed(33.3333333333333);
1237 break;
1238 case wxID_SPEED_50:
1239 set_speed(50);
1240 break;
1241 case wxID_SPEED_100:
1242 set_speed(100);
1243 break;
1244 case wxID_SPEED_150:
1245 set_speed(150);
1246 break;
1247 case wxID_SPEED_200:
1248 set_speed(200);
1249 break;
1250 case wxID_SPEED_300:
1251 set_speed(300);
1252 break;
1253 case wxID_SPEED_500:
1254 set_speed(500);
1255 break;
1256 case wxID_SPEED_1000:
1257 set_speed(1000);
1258 break;
1259 case wxID_SPEED_TURBO:
1260 set_speed(-1);
1261 break;
1262 case wxID_LOAD_LIBRARY: {
1263 std::string name = std::string("load ") + library_is_called;
1264 load_library(pick_file(this, name, ".", false));
1265 break;
1267 case wxID_SETTINGS:
1268 wxsetingsdialog_display(this, false);
1269 break;
1270 case wxID_SETTINGS_HOTKEYS:
1271 wxsetingsdialog_display(this, true);
1272 break;
1273 case wxID_LOAD_ROM_IMAGE:
1274 filename = pick_file_member(this, "Select new ROM image", rom_path());
1275 recent_roms->add(filename);
1276 platform::queue("unpause-emulator");
1277 platform::queue("reload-rom " + filename);
1278 return;
1279 case wxID_RELOAD_ROM_IMAGE:
1280 platform::queue("unpause-emulator");
1281 platform::queue("reload-rom");
1282 return;
1283 case wxID_NEW_MOVIE:
1284 show_projectwindow(this);
1285 return;
1286 case wxID_SHOW_MESSAGES:
1287 msg_window->reshow();
1288 return;
1289 case wxID_VUDISPLAY:
1290 open_vumeter_window(this);
1291 return;