Wxwidgets: Fix thread memory managment
[lsnes.git] / src / core / window.cpp
blob9073cd228857efb4f6b4fc6a1db07c944360de4c
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framerate.hpp"
4 #include "core/misc.hpp"
5 #include "core/render.hpp"
6 #include "core/window.hpp"
8 #include <fstream>
9 #include <string>
10 #include <deque>
11 #include <boost/iostreams/categories.hpp>
12 #include <boost/iostreams/copy.hpp>
13 #include <boost/iostreams/stream.hpp>
14 #include <boost/iostreams/stream_buffer.hpp>
15 #include <boost/iostreams/filter/symmetric.hpp>
16 #include <boost/iostreams/filter/zlib.hpp>
17 #include <boost/iostreams/filtering_stream.hpp>
18 #include <boost/iostreams/device/back_inserter.hpp>
20 #define MAXMESSAGES 5000
21 #define INIT_WIN_SIZE 6
23 mutex::holder::holder(mutex& m) throw()
24 : mut(m)
26 mut.lock();
29 mutex::holder::~holder() throw()
31 mut.unlock();
34 mutex::~mutex() throw()
38 mutex::mutex() throw()
42 condition::~condition() throw()
46 mutex& condition::associated() throw()
48 return assoc;
51 condition::condition(mutex& m)
52 : assoc(m)
56 thread_id::thread_id() throw()
60 thread_id::~thread_id() throw()
64 thread::thread() throw()
66 alive = true;
67 joined = false;
70 thread::~thread() throw()
74 bool thread::is_alive() throw()
76 return alive;
79 void* thread::join() throw()
81 if(!joined)
82 this->_join();
83 joined = true;
84 return returns;
87 void thread::notify_quit(void* ret) throw()
89 returns = ret;
90 alive = false;
93 keypress::keypress()
95 key1 = NULL;
96 key2 = NULL;
97 value = 0;
100 keypress::keypress(modifier_set mod, keygroup& _key, short _value)
102 modifiers = mod;
103 key1 = &_key;
104 key2 = NULL;
105 value = _value;
108 keypress::keypress(modifier_set mod, keygroup& _key, keygroup& _key2, short _value)
110 modifiers = mod;
111 key1 = &_key;
112 key2 = &_key2;
113 value = _value;
117 namespace
119 function_ptr_command<> identify_key("show-plugins", "Show plugins in use",
120 "Syntax: show-plugins\nShows plugins in use.\n",
121 []() throw(std::bad_alloc, std::runtime_error) {
122 messages << "Graphics:\t" << graphics_plugin::name << std::endl;
123 messages << "Sound:\t" << sound_plugin::name << std::endl;
124 messages << "Joystick:\t" << joystick_plugin::name << std::endl;
127 function_ptr_command<const std::string&> enable_sound("enable-sound", "Enable/Disable sound",
128 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
129 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
130 std::string s = args;
131 if(s == "on" || s == "true" || s == "1" || s == "enable" || s == "enabled") {
132 if(!platform::sound_initialized())
133 throw std::runtime_error("Sound failed to initialize and is disabled");
134 platform::sound_enable(true);
135 } else if(s == "off" || s == "false" || s == "0" || s == "disable" || s == "disabled") {
136 if(platform::sound_initialized())
137 platform::sound_enable(false);
138 } else
139 throw std::runtime_error("Bad sound setting");
142 function_ptr_command<const std::string&> set_sound_device("set-sound-device", "Set sound device",
143 "Syntax: set-sound-device <id>\nSet sound device to <id>.\n",
144 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
145 if(!platform::sound_initialized())
146 throw std::runtime_error("Sound failed to initialize and is disabled");
147 platform::set_sound_device(args);
150 function_ptr_command<> get_sound_devices("show-sound-devices", "Show sound devices",
151 "Syntax: show-sound-devices\nShow listing of available sound devices\n",
152 []() throw(std::bad_alloc, std::runtime_error) {
153 if(!platform::sound_initialized())
154 throw std::runtime_error("Sound failed to initialize and is disabled");
155 auto r = platform::get_sound_devices();
156 auto s = platform::get_sound_device();
157 std::string dname = "unknown";
158 if(r.count(s))
159 dname = r[s];
160 messages << "Detected " << r.size() << " sound output devices." << std::endl;
161 for(auto i : r)
162 messages << "Audio device " << i.first << ": " << i.second << std::endl;
163 messages << "Currently using device " << platform::get_sound_device() << " ("
164 << dname << ")" << std::endl;
167 function_ptr_command<> get_sound_status("show-sound-status", "Show sound status",
168 "Syntax: show-sound-status\nShow current sound status\n",
169 []() throw(std::bad_alloc, std::runtime_error) {
170 messages << "Sound plugin: " << sound_plugin::name << std::endl;
171 if(!platform::sound_initialized())
172 messages << "Sound initialization failed, sound disabled" << std::endl;
173 else {
174 auto r = platform::get_sound_devices();
175 auto s = platform::get_sound_device();
176 std::string dname = "unknown";
177 if(r.count(s))
178 dname = r[s];
179 messages << "Current sound device " << s << " (" << dname << ")" << std::endl;
183 emulator_status emustatus;
185 class window_output
187 public:
188 typedef char char_type;
189 typedef boost::iostreams::sink_tag category;
190 window_output(int* dummy)
194 void close()
198 std::streamsize write(const char* s, std::streamsize n)
200 size_t oldsize = stream.size();
201 stream.resize(oldsize + n);
202 memcpy(&stream[oldsize], s, n);
203 while(true) {
204 size_t lf = stream.size();
205 for(size_t i = 0; i < stream.size(); i++)
206 if(stream[i] == '\n') {
207 lf = i;
208 break;
210 if(lf == stream.size())
211 break;
212 std::string foo(stream.begin(), stream.begin() + lf);
213 platform::message(foo);
214 if(lf + 1 < stream.size())
215 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
216 stream.resize(stream.size() - lf - 1);
218 return n;
220 protected:
221 std::vector<char> stream;
224 class msgcallback : public messagebuffer::update_handler
226 public:
227 ~msgcallback() throw() {};
228 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
230 platform::notify_message();
232 } msg_callback_obj;
234 std::ofstream system_log;
235 bool sounds_enabled = true;
238 emulator_status& platform::get_emustatus() throw()
240 return emustatus;
243 void platform::sound_enable(bool enable) throw()
245 sound_plugin::enable(enable);
246 sounds_enabled = enable;
247 information_dispatch::do_sound_unmute(enable);
250 void platform::set_sound_device(const std::string& dev) throw()
252 try {
253 sound_plugin::set_device(dev);
254 } catch(std::exception& e) {
255 out() << "Error changing sound device: " << e.what() << std::endl;
257 //After failed change, we don't know what is selected.
258 information_dispatch::do_sound_change(sound_plugin::get_device());
261 bool platform::is_sound_enabled() throw()
263 return sounds_enabled;
267 void platform::init()
269 msgbuf.register_handler(msg_callback_obj);
270 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
271 time_t curtime = time(NULL);
272 struct tm* tm = localtime(&curtime);
273 char buffer[1024];
274 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
275 system_log << "-----------------------------------------------------------------------" << std::endl;
276 system_log << "lsnes started at " << buffer << std::endl;
277 system_log << "-----------------------------------------------------------------------" << std::endl;
278 do_init_font();
279 graphics_plugin::init();
280 sound_plugin::init();
281 joystick_plugin::init();
284 void platform::quit()
286 joystick_plugin::quit();
287 sound_plugin::quit();
288 graphics_plugin::quit();
289 msgbuf.unregister_handler(msg_callback_obj);
290 time_t curtime = time(NULL);
291 struct tm* tm = localtime(&curtime);
292 char buffer[1024];
293 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
294 system_log << "-----------------------------------------------------------------------" << std::endl;
295 system_log << "lsnes shutting down at " << buffer << std::endl;
296 system_log << "-----------------------------------------------------------------------" << std::endl;
297 system_log.close();
300 std::ostream& platform::out() throw(std::bad_alloc)
302 static std::ostream* cached = NULL;
303 int dummy;
304 if(!cached)
305 cached = new boost::iostreams::stream<window_output>(&dummy);
306 return *cached;
309 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
312 void platform::message(const std::string& msg) throw(std::bad_alloc)
314 mutex::holder h(msgbuf_lock());
315 std::string msg2 = msg;
316 while(msg2 != "") {
317 size_t s = msg2.find_first_of("\n");
318 std::string forlog;
319 if(s >= msg2.length()) {
320 msgbuf.add_message(forlog = msg2);
321 if(system_log)
322 system_log << forlog << std::endl;
323 break;
324 } else {
325 msgbuf.add_message(forlog = msg2.substr(0, s));
326 if(system_log)
327 system_log << forlog << std::endl;
328 msg2 = msg2.substr(s + 1);
333 void platform::fatal_error() throw()
335 time_t curtime = time(NULL);
336 struct tm* tm = localtime(&curtime);
337 char buffer[1024];
338 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
339 system_log << "-----------------------------------------------------------------------" << std::endl;
340 system_log << "lsnes paniced at " << buffer << std::endl;
341 system_log << "-----------------------------------------------------------------------" << std::endl;
342 system_log.close();
343 graphics_plugin::fatal_error();
344 exit(1);
347 namespace
349 mutex* queue_lock;
350 condition* queue_condition;
351 std::deque<keypress> keypresses;
352 std::deque<std::string> commands;
353 std::deque<std::pair<void(*)(void*), void*>> functions;
354 volatile bool normal_pause;
355 volatile bool modal_pause;
356 volatile uint64_t continue_time;
357 volatile uint64_t next_function;
358 volatile uint64_t functions_executed;
360 void init_threading()
362 if(!queue_lock)
363 queue_lock = &mutex::aquire();
364 if(!queue_condition)
365 queue_condition = &condition::aquire(*queue_lock);
368 void internal_run_queues(bool unlocked) throw()
370 init_threading();
371 if(!unlocked)
372 queue_lock->lock();
373 try {
374 //Flush keypresses.
375 while(!keypresses.empty()) {
376 keypress k = keypresses.front();
377 keypresses.pop_front();
378 queue_lock->unlock();
379 if(k.key1)
380 k.key1->set_position(k.value, k.modifiers);
381 if(k.key2)
382 k.key2->set_position(k.value, k.modifiers);
383 queue_lock->lock();
385 //Flush commands.
386 while(!commands.empty()) {
387 std::string c = commands.front();
388 commands.pop_front();
389 queue_lock->unlock();
390 command::invokeC(c);
391 queue_lock->lock();
393 //Flush functions.
394 while(!functions.empty()) {
395 std::pair<void(*)(void*), void*> f = functions.front();
396 functions.pop_front();
397 queue_lock->unlock();
398 f.first(f.second);
399 queue_lock->lock();
400 ++functions_executed;
402 queue_condition->signal();
403 } catch(std::bad_alloc& e) {
404 OOM_panic();
405 } catch(std::exception& e) {
406 std::cerr << "Fault inside platform::run_queues(): " << e.what() << std::endl;
407 exit(1);
409 if(!unlocked)
410 queue_lock->unlock();
414 #define MAXWAIT 10000
416 void platform::flush_command_queue() throw()
418 init_threading();
419 while(true) {
420 mutex::holder h(*queue_lock);
421 internal_run_queues(true);
422 uint64_t now = get_utime();
423 uint64_t waitleft = 0;
424 waitleft = (now < continue_time) ? (continue_time - now) : 0;
425 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
426 waitleft = (waitleft > MAXWAIT) ? MAXWAIT : waitleft;
427 if(waitleft > 0)
428 queue_condition->wait(waitleft);
429 else
430 return;
431 //If we had to wait, check queues at least once more.
435 void platform::set_paused(bool enable) throw()
437 normal_pause = enable;
440 void platform::wait(uint64_t usec) throw()
442 continue_time = get_utime() + usec;
443 init_threading();
444 while(true) {
445 mutex::holder h(*queue_lock);
446 internal_run_queues(true);
447 uint64_t now = get_utime();
448 uint64_t waitleft = 0;
449 waitleft = (now < continue_time) ? (continue_time - now) : 0;
450 waitleft = (waitleft > MAXWAIT) ? MAXWAIT : waitleft;
451 if(waitleft > 0)
452 queue_condition->wait(waitleft);
453 else
454 return;
458 void platform::cancel_wait() throw()
460 init_threading();
461 continue_time = 0;
462 mutex::holder h(*queue_lock);
463 queue_condition->signal();
466 void platform::set_modal_pause(bool enable) throw()
468 modal_pause = enable;
471 void platform::queue(const keypress& k) throw(std::bad_alloc)
473 init_threading();
474 mutex::holder h(*queue_lock);
475 keypresses.push_back(k);
476 queue_condition->signal();
479 void platform::queue(const std::string& c) throw(std::bad_alloc)
481 init_threading();
482 mutex::holder h(*queue_lock);
483 commands.push_back(c);
484 queue_condition->signal();
487 void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
489 init_threading();
490 mutex::holder h(*queue_lock);
491 ++next_function;
492 functions.push_back(std::make_pair(f, arg));
493 queue_condition->signal();
494 if(sync)
495 while(functions_executed < next_function)
496 queue_condition->wait(10000);
499 void platform::run_queues() throw()
501 internal_run_queues(false);
504 namespace
506 mutex* _msgbuf_lock;
507 screen* our_screen;
509 void trigger_repaint()
511 graphics_plugin::notify_screen();
514 struct painter_listener : public information_dispatch
516 painter_listener();
517 void on_set_screen(screen& scr);
518 void on_screen_update();
519 void on_status_update();
520 } x;
522 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
524 void painter_listener::on_set_screen(screen& scr)
526 our_screen = &scr;
529 void painter_listener::on_screen_update()
531 trigger_repaint();
534 void painter_listener::on_status_update()
536 graphics_plugin::notify_status();
539 struct handle_mouse_request
541 long x;
542 long y;
543 uint32_t mask;
546 void _handle_mouse(void* args)
548 struct handle_mouse_request* x = reinterpret_cast<struct handle_mouse_request*>(args);
549 information_dispatch::do_click(x->x, x->y, x->mask);
553 void send_mouse_click(long x, long y, uint32_t buttons)
555 struct handle_mouse_request z;
556 z.x = x;
557 z.y = y;
558 z.mask = buttons;
559 platform::queue(_handle_mouse, &z, true);
562 mutex& platform::msgbuf_lock() throw()
564 if(!_msgbuf_lock)
565 try {
566 _msgbuf_lock = &mutex::aquire();
567 } catch(...) {
568 OOM_panic();
570 return *_msgbuf_lock;
573 void platform::screen_set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
575 if(!our_screen)
576 return;
577 if(our_screen->palette_r == rshift &&
578 our_screen->palette_g == gshift &&
579 our_screen->palette_b == bshift)
580 return;
581 our_screen->set_palette(rshift, gshift, bshift);
582 trigger_repaint();