Use urandom / rtlgenrandom
[lsnes.git] / src / platform / wxwidgets / main.cpp
blobb2e0928cd19b4f760333a954a5bcdad46b895c34
1 #include <wx/wx.h>
3 #include "lsnes.hpp"
5 #include "core/command.hpp"
6 #include "core/controller.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/framerate.hpp"
9 #include "core/joystickapi.hpp"
10 #include "core/keymapper.hpp"
11 #include "core/loadlib.hpp"
12 #include "lua/lua.hpp"
13 #include "core/mainloop.hpp"
14 #include "core/misc.hpp"
15 #include "core/movie.hpp"
16 #include "core/moviefile-common.hpp"
17 #include "core/moviedata.hpp"
18 #include "core/rom.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
21 #include "interface/romtype.hpp"
22 #include "library/crandom.hpp"
23 #include "library/string.hpp"
24 #include "library/threads.hpp"
25 #include "library/utf8.hpp"
26 #include "library/zip.hpp"
28 #include "platform/wxwidgets/settings-common.hpp"
29 #include "platform/wxwidgets/platform.hpp"
30 #include "platform/wxwidgets/window_messages.hpp"
31 #include "platform/wxwidgets/window_status.hpp"
32 #include "platform/wxwidgets/window_mainwindow.hpp"
34 #include <cassert>
35 #include <boost/lexical_cast.hpp>
37 #include <wx/wx.h>
38 #include <wx/event.h>
39 #include <wx/control.h>
40 #include <wx/combobox.h>
41 #include <wx/cmdline.h>
42 #include <iostream>
44 #define UISERV_REFRESH_TITLE 9990
45 #define UISERV_RESIZED 9991
46 #define UISERV_UIFUN 9992
47 //#define UISERV_UI_IRQ 9993 Not in use anymore, can be recycled.
48 #define UISERV_EXIT 9994
49 #define UISERV_UPDATE_STATUS 9995
50 #define UISERV_UPDATE_MESSAGES 9996
51 #define UISERV_UPDATE_SCREEN 9997
52 #define UISERV_PANIC 9998
53 #define UISERV_ERROR 9999
55 wxwin_messages* msg_window;
56 wxwin_mainwindow* main_window;
57 std::string our_rom_name;
59 bool wxwidgets_exiting = false;
61 namespace
63 threads::id ui_thread;
64 volatile bool panic_ack = false;
65 std::string error_message_text;
66 volatile bool modal_dialog_confirm;
67 volatile bool modal_dialog_active;
68 threads::lock ui_mutex;
69 threads::cv ui_condition;
70 threads::thread* joystick_thread_handle;
71 bool if_update_messages = false;
72 bool if_update_screen = false;
73 bool if_update_status = false;
74 threads::lock if_mutex;
76 void* joystick_thread(int _args)
78 joystick_driver_thread_fn();
79 return NULL;
82 struct uiserv_event : public wxEvent
84 uiserv_event(int code)
86 SetId(code);
89 wxEvent* Clone() const
91 return new uiserv_event(*this);
95 class ui_services_type : public wxEvtHandler
97 bool ProcessEvent(wxEvent& event);
100 struct ui_queue_entry
102 void(*fn)(void*);
103 void* arg;
106 std::list<ui_queue_entry> ui_queue;
108 bool ui_services_type::ProcessEvent(wxEvent& event)
110 int c = event.GetId();
111 if(c == UISERV_PANIC) {
112 //We need to panic.
113 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"), wxICON_ERROR |
114 wxOK);
115 panic_ack = true;
116 } else if(c == UISERV_REFRESH_TITLE) {
117 if(main_window)
118 main_window->refresh_title();
119 } else if(c == UISERV_RESIZED) {
120 if(main_window)
121 main_window->notify_resized();
122 } else if(c == UISERV_ERROR) {
123 std::string text = error_message_text;
124 wxMessageBox(towxstring(text), _T("lsnes: Error"), wxICON_EXCLAMATION | wxOK, main_window);
125 } else if(c == UISERV_UPDATE_MESSAGES) {
126 { threads::alock h(if_mutex); if_update_messages = false; }
127 if(msg_window)
128 msg_window->notify_update();
129 } else if(c == UISERV_UPDATE_STATUS) {
130 { threads::alock h(if_mutex); if_update_status = false; }
131 if(main_window)
132 main_window->notify_update_status();
133 wxeditor_movie_update();
134 wxeditor_hexeditor_update();
135 } else if(c == UISERV_UPDATE_SCREEN) {
136 { threads::alock h(if_mutex); if_update_screen = false; }
137 if(main_window)
138 main_window->notify_update();
139 wxwindow_memorysearch_update();
140 wxwindow_tasinput_update();
141 } else if(c == UISERV_EXIT) {
142 if(main_window)
143 main_window->notify_exit();
144 } else if(c == UISERV_UIFUN) {
145 std::list<ui_queue_entry>::iterator i;
146 ui_queue_entry e;
147 queue_synchronous_fn_warning = true;
148 back:
150 threads::alock h(ui_mutex);
151 if(ui_queue.empty())
152 goto end;
153 i = ui_queue.begin();
154 e = *i;
155 ui_queue.erase(i);
157 e.fn(e.arg);
158 goto back;
159 end:
160 queue_synchronous_fn_warning = false;
162 return true;
165 ui_services_type* ui_services;
167 void post_ui_event(int code)
169 //Coalesce some messages.
170 if(code == UISERV_UPDATE_MESSAGES) {
171 threads::alock h(if_mutex);
172 if(if_update_messages) return;
173 if_update_messages = true;
175 if(code == UISERV_UPDATE_SCREEN) {
176 threads::alock h(if_mutex);
177 if(if_update_screen) return;
178 if_update_screen = true;
180 if(code == UISERV_UPDATE_STATUS) {
181 threads::alock h(if_mutex);
182 if(if_update_status) return;
183 if_update_status = true;
185 uiserv_event uic(code);
186 wxPostEvent(ui_services, uic);
189 void handle_config_line(std::string line)
191 regex_results r;
192 if(r = regex("SET[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
193 lsnes_vsetc.set(r[1], r[2], true);
194 messages << "Setting " << r[1] << " set to " << r[2] << std::endl;
195 } else if(r = regex("ALIAS[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
196 if(!lsnes_cmd.valid_alias_name(r[1])) {
197 messages << "Illegal alias name " << r[1] << std::endl;
198 return;
200 std::string tmp = lsnes_cmd.get_alias_for(r[1]);
201 tmp = tmp + r[2] + "\n";
202 lsnes_cmd.set_alias_for(r[1], tmp);
203 messages << r[1] << " aliased to " << r[2] << std::endl;
204 } else if(r = regex("BIND[ \t]+([^/]*)/([^|]*)\\|([^ \t]+)[ \t]+(.*)", line)) {
205 std::string tmp = r[4];
206 regex_results r2 = regex("(load|load-smart|load-readonly|load-preserve|load-state"
207 "|load-movie|save-state|save-movie)[ \t]+\\$\\{project\\}(.*)\\.lsmv", tmp);
208 if(r2) tmp = r2[1] + " $SLOT:" + r2[2];
209 lsnes_mapper.bind(r[1], r[2], r[3], tmp);
210 if(r[1] != "" || r[2] != "")
211 messages << r[1] << "/" << r[2] << " ";
212 messages << r[3] << " bound to '" << tmp << "'" << std::endl;
213 } else if(r = regex("BUTTON[ \t]+([^ \t]+)[ \t](.*)", line)) {
214 keyboard::ctrlrkey* ckey = lsnes_mapper.get_controllerkey(r[2]);
215 if(ckey) {
216 ckey->append(r[1]);
217 messages << r[1] << " bound (button) to " << r[2] << std::endl;
218 } else
219 button_keys[r[2]] = r[1];
220 } else if(r = regex("PREFER[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
221 if(r[2] != "") {
222 core_selections[r[1]] = r[2];
223 messages << "Prefer " << r[2] << " for " << r[1] << std::endl;
225 } else
226 (stringfmt() << "Unrecognized directive: " << line).throwex();
229 void load_configuration()
231 std::string cfg = get_config_path() + "/lsneswxw.cfg";
232 std::ifstream cfgfile(cfg.c_str());
233 std::string line;
234 size_t lineno = 1;
235 while(std::getline(cfgfile, line)) {
236 try {
237 handle_config_line(line);
238 } catch(std::exception& e) {
239 messages << "Error processing line " << lineno << ": " << e.what() << std::endl;
241 lineno++;
243 refresh_alias_binds();
244 lsnes_uri_rewrite.load(get_config_path() + "/lsnesurirewrite.cfg");
247 void save_configuration()
249 std::string cfg = get_config_path() + "/lsneswxw.cfg";
250 std::string cfgtmp = cfg + ".tmp";
251 std::ofstream cfgfile(cfgtmp.c_str());
252 //Settings.
253 for(auto i : lsnes_vsetc.get_all())
254 cfgfile << "SET " << i.first << " " << i.second << std::endl;
255 //Aliases.
256 for(auto i : lsnes_cmd.get_aliases()) {
257 std::string old_alias_value = lsnes_cmd.get_alias_for(i);
258 while(old_alias_value != "") {
259 std::string aliasline;
260 size_t s = old_alias_value.find_first_of("\n");
261 if(s < old_alias_value.length()) {
262 aliasline = old_alias_value.substr(0, s);
263 old_alias_value = old_alias_value.substr(s + 1);
264 } else {
265 aliasline = old_alias_value;
266 old_alias_value = "";
268 cfgfile << "ALIAS " << i << " " << aliasline << std::endl;
271 //Keybindings.
272 for(auto i : lsnes_mapper.get_bindings())
273 cfgfile << "BIND " << std::string(i) << " " << lsnes_mapper.get(i) << std::endl;
274 //Buttons.
275 for(auto i : lsnes_mapper.get_controller_keys()) {
276 std::string b;
277 unsigned idx = 0;
278 while((b = i->get_string(idx++)) != "")
279 cfgfile << "BUTTON " << b << " " << i->get_command() << std::endl;
281 for(auto i : button_keys)
282 cfgfile << "BUTTON " << i.second << " " << i.first << std::endl;
283 for(auto i : core_selections)
284 if(i.second != "")
285 cfgfile << "PREFER " << i.first << " " << i.second << std::endl;
286 if(!cfgfile) {
287 show_message_ok(NULL, "Error Saving configuration", "Error saving configuration",
288 wxICON_EXCLAMATION);
289 return;
291 cfgfile.close();
292 zip::rename_overwrite(cfgtmp.c_str(), cfg.c_str());
293 //Last save.
294 std::ofstream lsave(get_config_path() + "/" + our_rom_name + ".ls");
295 lsave << last_save;
296 lsnes_uri_rewrite.save(get_config_path() + "/lsnesurirewrite.cfg");
299 void* eloop_helper(int x)
301 platform::dummy_event_loop();
302 return NULL;
305 std::string get_loaded_movie(const std::vector<std::string>& cmdline)
307 for(auto i : cmdline)
308 if(!i.empty() && i[0] != '-')
309 return i;
310 return "";
314 wxString towxstring(const std::string& str) throw(std::bad_alloc)
316 return wxString(str.c_str(), wxConvUTF8);
319 std::string tostdstring(const wxString& str) throw(std::bad_alloc)
321 return std::string(str.mb_str(wxConvUTF8));
324 wxString towxstring(const std::u32string& str) throw(std::bad_alloc)
326 return wxString(utf8::to8(str).c_str(), wxConvUTF8);
329 std::u32string tou32string(const wxString& str) throw(std::bad_alloc)
331 return utf8::to32(std::string(str.mb_str(wxConvUTF8)));
334 std::string pick_archive_member(wxWindow* parent, const std::string& filename) throw(std::bad_alloc)
336 //Did we pick a .zip file?
337 std::string f;
338 try {
339 zip::reader zr(filename);
340 std::vector<wxString> files;
341 for(auto i : zr)
342 files.push_back(towxstring(i));
343 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(parent, wxT("Select file within .zip"),
344 wxT("Select member"), files.size(), &files[0]);
345 if(d2->ShowModal() == wxID_CANCEL) {
346 d2->Destroy();
347 return "";
349 f = filename + "/" + tostdstring(d2->GetStringSelection());
350 d2->Destroy();
351 } catch(...) {
352 //Ignore error.
353 f = filename;
355 return f;
358 void signal_program_exit()
360 post_ui_event(UISERV_EXIT);
363 void signal_resize_needed()
365 post_ui_event(UISERV_RESIZED);
369 static const wxCmdLineEntryDesc dummy_descriptor_table[] = {
370 { wxCMD_LINE_PARAM, NULL, NULL, NULL, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL |
371 wxCMD_LINE_PARAM_MULTIPLE },
372 { wxCMD_LINE_NONE }
375 class lsnes_app : public wxApp
377 public:
378 lsnes_app();
379 virtual bool OnInit();
380 virtual int OnExit();
381 virtual void OnInitCmdLine(wxCmdLineParser& parser);
382 virtual bool OnCmdLineParsed(wxCmdLineParser& parser);
383 private:
384 bool settings_mode;
385 bool pluginmanager_mode;
386 std::string c_rom;
387 std::string c_file;
388 std::vector<std::string> cmdline;
389 std::map<std::string, std::string> c_settings;
390 std::vector<std::string> c_lua;
391 bool exit_immediately;
392 bool fullscreen_mode;
393 bool start_unpaused;
396 IMPLEMENT_APP(lsnes_app)
398 lsnes_app::lsnes_app()
400 settings_mode = false;
401 pluginmanager_mode = false;
402 exit_immediately = false;
403 fullscreen_mode = false;
404 start_unpaused = false;
407 void lsnes_app::OnInitCmdLine(wxCmdLineParser& parser)
409 parser.SetDesc(dummy_descriptor_table);
410 parser.SetSwitchChars(wxT(""));
413 bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser)
415 for(size_t i = 0; i< parser.GetParamCount(); i++)
416 cmdline.push_back(tostdstring(parser.GetParam(i)));
417 for(auto i: cmdline) {
418 regex_results r;
419 if(i == "--help" || i == "-h") {
420 std::cout << "--settings: Show the settings dialog" << std::endl;
421 std::cout << "--pluginmanager: Show the plugin manager" << std::endl;
422 std::cout << "--fullscreen: Start fullscreen" << std::endl;
423 std::cout << "--unpause: Start unpaused (only if ROM is loaded)" << std::endl;
424 std::cout << "--rom=<filename>: Load specified ROM on startup" << std::endl;
425 std::cout << "--load=<filename>: Load specified save/movie on starup" << std::endl;
426 std::cout << "--lua=<filename>: Load specified Lua script on startup" << std::endl;
427 std::cout << "--set=<a>=<b>: Set setting <a> to value <b>" << std::endl;
428 std::cout << "<filename>: Load specified ROM on startup" << std::endl;
429 exit_immediately = true;
430 return true;
432 if(i == "--settings")
433 settings_mode = true;
434 if(i == "--unpause")
435 start_unpaused = true;
436 if(i == "--fullscreen")
437 fullscreen_mode = true;
438 if(i == "--pluginmanager")
439 pluginmanager_mode = true;
440 if(r = regex("--set=([^=]+)=(.+)", i))
441 c_settings[r[1]] = r[2];
442 if(r = regex("--lua=(.+)", i))
443 c_lua.push_back(r[1]);
445 return true;
448 bool lsnes_app::OnInit()
450 wxApp::OnInit();
451 if(exit_immediately)
452 return false;
454 try {
455 crandom::init();
456 } catch(std::exception& e) {
457 show_message_ok(NULL, "RNG error", "Error initializing system RNG", wxICON_ERROR);
458 return false;
461 reached_main();
462 set_random_seed();
463 bring_app_foreground();
465 if(pluginmanager_mode)
466 if(!wxeditor_plugin_manager_display(NULL))
467 return false;
469 ui_services = new ui_services_type();
471 ui_thread = threads::this_id();
472 platform::init();
474 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
476 loaded_rom dummy_rom;
477 std::map<std::string, std::string> settings;
478 auto ctrldata = dummy_rom.rtype->controllerconfig(settings);
479 port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
481 reinitialize_buttonmap();
482 controls.set_ports(ports);
484 std::string cfgpath = get_config_path();
485 autoload_libraries([](const std::string& libname, const std::string& error, bool system) {
486 show_message_ok(NULL, "Error loading plugin " + libname, "Error loading '" + libname + "'\n\n" +
487 error, wxICON_EXCLAMATION);
488 if(!system)
489 wxeditor_plugin_manager_notify_fail(libname);
491 messages << "Saving per-user data to: " << get_config_path() << std::endl;
492 messages << "--- Loading configuration --- " << std::endl;
493 load_configuration();
494 messages << "--- End running lsnesrc --- " << std::endl;
496 if(settings_mode) {
497 //We got to boot this up quite a bit to get the joystick driver working.
498 //In practicular, we need joystick thread and emulator thread in pause.
499 joystick_thread_handle = new threads::thread(joystick_thread, 6);
500 threads::thread* dummy_loop = new threads::thread(eloop_helper, 8);
501 display_settings_dialog(NULL, NULL);
502 platform::exit_dummy_event_loop();
503 joystick_driver_signal();
504 joystick_thread_handle->join();
505 dummy_loop->join();
506 save_configuration();
507 return false;
509 init_lua();
511 joystick_thread_handle = new threads::thread(joystick_thread, 7);
513 msg_window = new wxwin_messages();
514 msg_window->Show();
516 init_main_callbacks();
517 const std::string movie_file = get_loaded_movie(cmdline);
518 loaded_rom rom;
519 try {
520 moviefile mov;
521 rom = construct_rom(movie_file, cmdline);
522 rom.load(c_settings, mov.movie_rtc_second, mov.movie_rtc_subsecond);
523 } catch(std::exception& e) {
524 std::cerr << "Can't load ROM: " << e.what() << std::endl;
525 show_message_ok(NULL, "Error loading ROM", std::string("Error loading ROM:\n\n") +
526 e.what(), wxICON_EXCLAMATION);
527 quit_lua(); //Don't crash.
528 return false;
531 moviefile* mov = NULL;
532 if(movie_file != "")
533 try {
534 mov = new moviefile(movie_file, *rom.rtype);
535 rom.load(mov->settings, mov->movie_rtc_second, mov->movie_rtc_subsecond);
536 } catch(std::exception& e) {
537 std::cerr << "Can't load state: " << e.what() << std::endl;
538 show_message_ok(NULL, "Error loading movie", std::string("Error loading movie:\n\n") +
539 e.what(), wxICON_EXCLAMATION);
540 quit_lua(); //Don't crash.
541 return false;
543 else {
544 mov = new moviefile(rom, c_settings, DEFAULT_RTC_SECOND, DEFAULT_RTC_SUBSECOND);
546 our_rom = rom;
547 mov->start_paused = start_unpaused ? !(rom.rtype && !rom.rtype->isnull()) : true;
548 for(auto i : c_lua)
549 lua_add_startup_script(i);
550 boot_emulator(rom, *mov, fullscreen_mode);
551 return true;
554 int lsnes_app::OnExit()
556 if(settings_mode)
557 return 0;
558 //NULL these so no further messages will be sent.
559 auto x = msg_window;
560 msg_window = NULL;
561 main_window = NULL;
562 if(x)
563 x->Destroy();
564 save_configuration();
565 information_dispatch::do_dump_end();
566 quit_lua();
567 movb.release_memory();
568 joystick_driver_signal();
569 joystick_thread_handle->join();
570 platform::quit();
571 cleanup_all_keys();
572 cleanup_keymapper();
573 kill_alias_binds();
574 deinitialize_wx_keyboard();
575 return 0;
578 void do_save_configuration()
580 save_configuration();
583 namespace
585 struct _graphics_driver drv = {
586 .init = []() -> void {
587 initialize_wx_keyboard();
589 .quit = []() -> void {},
590 .notify_message = []() -> void
592 post_ui_event(UISERV_UPDATE_MESSAGES);
594 .notify_status = []() -> void
596 post_ui_event(UISERV_UPDATE_STATUS);
598 .notify_screen = []() -> void
600 post_ui_event(UISERV_UPDATE_SCREEN);
602 .error_message = [](const std::string& text) -> void {
603 error_message_text = text;
604 post_ui_event(UISERV_ERROR);
606 .fatal_error = []() -> void {
607 //Fun: This can be called from any thread!
608 if(ui_thread == threads::this_id()) {
609 //UI thread.
610 platform::set_modal_pause(true);
611 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"),
612 wxICON_ERROR | wxOK);
613 } else {
614 //Emulation thread panic. Signal the UI thread.
615 post_ui_event(UISERV_PANIC);
616 while(!panic_ack);
619 .name = []() -> const char* { return "wxwidgets graphics plugin"; },
620 .action_updated = []()
622 runuifun([]() -> void { main_window->action_updated(); });
624 .request_rom = [](rom_request& req)
626 rom_request* _req = &req;
627 threads::lock lock;
628 threads::cv cv;
629 bool done = false;
630 threads::alock h(lock);
631 runuifun([_req, &lock, &cv, &done]() -> void {
632 try {
633 main_window->request_rom(*_req);
634 } catch(...) {
635 _req->canceled = true;
637 threads::alock h(lock);
638 done = true;
639 cv.notify_all();
641 while(!done)
642 cv.wait(h);
645 struct graphics_driver _drv(drv);
648 void signal_core_change()
650 post_ui_event(UISERV_REFRESH_TITLE);
653 void _runuifun_async(void (*fn)(void*), void* arg)
655 threads::alock h(ui_mutex);
656 ui_queue_entry e;
657 e.fn = fn;
658 e.arg = arg;
659 ui_queue.push_back(e);
660 post_ui_event(UISERV_UIFUN);
664 canceled_exception::canceled_exception() : std::runtime_error("Dialog canceled") {}
666 std::string pick_file(wxWindow* parent, const std::string& title, const std::string& startdir)
668 wxString _title = towxstring(title);
669 wxString _startdir = towxstring(startdir);
670 std::string filespec;
671 filespec = "All files|*";
672 wxFileDialog* d = new wxFileDialog(parent, _title, _startdir, wxT(""), towxstring(filespec), wxFD_OPEN);
673 if(d->ShowModal() == wxID_CANCEL)
674 throw canceled_exception();
675 std::string filename = tostdstring(d->GetPath());
676 d->Destroy();
677 if(filename == "")
678 throw canceled_exception();
679 return filename;
682 std::string pick_file_member(wxWindow* parent, const std::string& title, const std::string& startdir)
684 std::string filename = pick_file(parent, title, startdir);
685 //Did we pick a .zip file?
686 if(!regex_match(".*\\.[zZ][iI][pP]", filename))
687 return filename; //Not a ZIP.
688 try {
689 zip::reader zr(filename);
690 std::vector<std::string> files;
691 for(auto i : zr)
692 files.push_back(i);
693 filename = filename + "/" + pick_among(parent, "Select member", "Select file within .zip", files);
694 } catch(canceled_exception& e) {
695 //Throw these forward.
696 throw;
697 } catch(...) {
698 //Ignore error.
700 return filename;
703 std::string pick_among(wxWindow* parent, const std::string& title, const std::string& prompt,
704 const std::vector<std::string>& choices, unsigned defaultchoice)
706 std::vector<wxString> _choices;
707 for(auto i : choices)
708 _choices.push_back(towxstring(i));
709 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(parent, towxstring(prompt), towxstring(title),
710 _choices.size(), &_choices[0]);
711 d2->SetSelection(defaultchoice);
712 if(d2->ShowModal() == wxID_CANCEL) {
713 d2->Destroy();
714 throw canceled_exception();
716 std::string out = tostdstring(d2->GetStringSelection());
717 d2->Destroy();
718 return out;
721 std::string pick_text(wxWindow* parent, const std::string& title, const std::string& prompt, const std::string& dflt,
722 bool multiline)
724 wxTextEntryDialog* d2 = new wxTextEntryDialog(parent, towxstring(prompt), towxstring(title), towxstring(dflt),
725 wxOK | wxCANCEL | wxCENTRE | (multiline ? wxTE_MULTILINE : 0));
726 if(d2->ShowModal() == wxID_CANCEL) {
727 d2->Destroy();
728 throw canceled_exception();
730 std::string text = tostdstring(d2->GetValue());
731 d2->Destroy();
732 return text;
735 void show_message_ok(wxWindow* parent, const std::string& title, const std::string& text, int icon)
737 wxMessageDialog* d3 = new wxMessageDialog(parent, towxstring(text), towxstring(title), wxOK | icon);
738 d3->ShowModal();
739 d3->Destroy();