Fix the speed throttle
[lsnes.git] / src / core / window.cpp
blob34cf72337180112a24e70b00ee919664ff778a74
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 <iostream>
10 #include <string>
11 #include <deque>
12 #include <boost/iostreams/categories.hpp>
13 #include <boost/iostreams/copy.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #include <boost/iostreams/stream_buffer.hpp>
16 #include <boost/iostreams/filter/symmetric.hpp>
17 #include <boost/iostreams/filter/zlib.hpp>
18 #include <boost/iostreams/filtering_stream.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
21 #define MAXMESSAGES 5000
22 #define INIT_WIN_SIZE 6
24 mutex::holder::holder(mutex& m) throw()
25 : mut(m)
27 mut.lock();
30 mutex::holder::~holder() throw()
32 mut.unlock();
35 mutex::~mutex() throw()
39 mutex::mutex() throw()
43 condition::~condition() throw()
47 mutex& condition::associated() throw()
49 return assoc;
52 condition::condition(mutex& m)
53 : assoc(m)
57 thread_id::thread_id() throw()
61 thread_id::~thread_id() throw()
65 thread::thread() throw()
67 alive = true;
68 joined = false;
71 thread::~thread() throw()
75 bool thread::is_alive() throw()
77 return alive;
80 void* thread::join() throw()
82 if(!joined)
83 this->_join();
84 joined = true;
85 return returns;
88 void thread::notify_quit(void* ret) throw()
90 returns = ret;
91 alive = false;
94 keypress::keypress()
96 key1 = NULL;
97 key2 = NULL;
98 value = 0;
101 keypress::keypress(modifier_set mod, keygroup& _key, short _value)
103 modifiers = mod;
104 key1 = &_key;
105 key2 = NULL;
106 value = _value;
109 keypress::keypress(modifier_set mod, keygroup& _key, keygroup& _key2, short _value)
111 modifiers = mod;
112 key1 = &_key;
113 key2 = &_key2;
114 value = _value;
118 namespace
120 function_ptr_command<> identify_key("show-plugins", "Show plugins in use",
121 "Syntax: show-plugins\nShows plugins in use.\n",
122 []() throw(std::bad_alloc, std::runtime_error) {
123 messages << "Graphics:\t" << graphics_plugin::name << std::endl;
124 messages << "Sound:\t" << sound_plugin::name << std::endl;
125 messages << "Joystick:\t" << joystick_plugin::name << std::endl;
128 function_ptr_command<const std::string&> enable_sound("enable-sound", "Enable/Disable sound",
129 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
130 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
131 std::string s = args;
132 if(s == "on" || s == "true" || s == "1" || s == "enable" || s == "enabled") {
133 if(!platform::sound_initialized())
134 throw std::runtime_error("Sound failed to initialize and is disabled");
135 platform::sound_enable(true);
136 } else if(s == "off" || s == "false" || s == "0" || s == "disable" || s == "disabled") {
137 if(platform::sound_initialized())
138 platform::sound_enable(false);
139 } else
140 throw std::runtime_error("Bad sound setting");
143 function_ptr_command<const std::string&> set_sound_device("set-sound-device", "Set sound device",
144 "Syntax: set-sound-device <id>\nSet sound device to <id>.\n",
145 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
146 if(!platform::sound_initialized())
147 throw std::runtime_error("Sound failed to initialize and is disabled");
148 platform::set_sound_device(args);
151 function_ptr_command<> get_sound_devices("show-sound-devices", "Show sound devices",
152 "Syntax: show-sound-devices\nShow listing of available sound devices\n",
153 []() throw(std::bad_alloc, std::runtime_error) {
154 if(!platform::sound_initialized())
155 throw std::runtime_error("Sound failed to initialize and is disabled");
156 auto r = platform::get_sound_devices();
157 auto s = platform::get_sound_device();
158 std::string dname = "unknown";
159 if(r.count(s))
160 dname = r[s];
161 messages << "Detected " << r.size() << " sound output devices." << std::endl;
162 for(auto i : r)
163 messages << "Audio device " << i.first << ": " << i.second << std::endl;
164 messages << "Currently using device " << platform::get_sound_device() << " ("
165 << dname << ")" << std::endl;
168 function_ptr_command<> get_sound_status("show-sound-status", "Show sound status",
169 "Syntax: show-sound-status\nShow current sound status\n",
170 []() throw(std::bad_alloc, std::runtime_error) {
171 messages << "Sound plugin: " << sound_plugin::name << std::endl;
172 if(!platform::sound_initialized())
173 messages << "Sound initialization failed, sound disabled" << std::endl;
174 else {
175 auto r = platform::get_sound_devices();
176 auto s = platform::get_sound_device();
177 std::string dname = "unknown";
178 if(r.count(s))
179 dname = r[s];
180 messages << "Current sound device " << s << " (" << dname << ")" << std::endl;
184 emulator_status emustatus;
186 class window_output
188 public:
189 typedef char char_type;
190 typedef boost::iostreams::sink_tag category;
191 window_output(int* dummy)
195 void close()
199 std::streamsize write(const char* s, std::streamsize n)
201 size_t oldsize = stream.size();
202 stream.resize(oldsize + n);
203 memcpy(&stream[oldsize], s, n);
204 while(true) {
205 size_t lf = stream.size();
206 for(size_t i = 0; i < stream.size(); i++)
207 if(stream[i] == '\n') {
208 lf = i;
209 break;
211 if(lf == stream.size())
212 break;
213 std::string foo(stream.begin(), stream.begin() + lf);
214 platform::message(foo);
215 if(lf + 1 < stream.size())
216 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
217 stream.resize(stream.size() - lf - 1);
219 return n;
221 protected:
222 std::vector<char> stream;
225 class msgcallback : public messagebuffer::update_handler
227 public:
228 ~msgcallback() throw() {};
229 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
231 platform::notify_message();
233 } msg_callback_obj;
235 std::ofstream system_log;
236 bool sounds_enabled = true;
239 emulator_status& platform::get_emustatus() throw()
241 return emustatus;
244 void platform::sound_enable(bool enable) throw()
246 sound_plugin::enable(enable);
247 sounds_enabled = enable;
248 information_dispatch::do_sound_unmute(enable);
251 void platform::set_sound_device(const std::string& dev) throw()
253 try {
254 sound_plugin::set_device(dev);
255 } catch(std::exception& e) {
256 out() << "Error changing sound device: " << e.what() << std::endl;
258 //After failed change, we don't know what is selected.
259 information_dispatch::do_sound_change(sound_plugin::get_device());
262 bool platform::is_sound_enabled() throw()
264 return sounds_enabled;
268 void platform::init()
270 msgbuf.register_handler(msg_callback_obj);
271 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
272 time_t curtime = time(NULL);
273 struct tm* tm = localtime(&curtime);
274 char buffer[1024];
275 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
276 system_log << "-----------------------------------------------------------------------" << std::endl;
277 system_log << "lsnes started at " << buffer << std::endl;
278 system_log << "-----------------------------------------------------------------------" << std::endl;
279 do_init_font();
280 graphics_plugin::init();
281 sound_plugin::init();
282 joystick_plugin::init();
285 void platform::quit()
287 joystick_plugin::quit();
288 sound_plugin::quit();
289 graphics_plugin::quit();
290 msgbuf.unregister_handler(msg_callback_obj);
291 time_t curtime = time(NULL);
292 struct tm* tm = localtime(&curtime);
293 char buffer[1024];
294 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
295 system_log << "-----------------------------------------------------------------------" << std::endl;
296 system_log << "lsnes shutting down at " << buffer << std::endl;
297 system_log << "-----------------------------------------------------------------------" << std::endl;
298 system_log.close();
301 std::ostream& platform::out() throw(std::bad_alloc)
303 static std::ostream* cached = NULL;
304 int dummy;
305 if(!cached)
306 cached = new boost::iostreams::stream<window_output>(&dummy);
307 return *cached;
310 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
313 void platform::message(const std::string& msg) throw(std::bad_alloc)
315 mutex::holder h(msgbuf_lock());
316 std::string msg2 = msg;
317 while(msg2 != "") {
318 size_t s = msg2.find_first_of("\n");
319 std::string forlog;
320 if(s >= msg2.length()) {
321 msgbuf.add_message(forlog = msg2);
322 if(system_log)
323 system_log << forlog << std::endl;
324 break;
325 } else {
326 msgbuf.add_message(forlog = msg2.substr(0, s));
327 if(system_log)
328 system_log << forlog << std::endl;
329 msg2 = msg2.substr(s + 1);
334 void platform::fatal_error() throw()
336 time_t curtime = time(NULL);
337 struct tm* tm = localtime(&curtime);
338 char buffer[1024];
339 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
340 system_log << "-----------------------------------------------------------------------" << std::endl;
341 system_log << "lsnes paniced at " << buffer << std::endl;
342 system_log << "-----------------------------------------------------------------------" << std::endl;
343 system_log.close();
344 graphics_plugin::fatal_error();
345 exit(1);
348 namespace
350 mutex* queue_lock;
351 condition* queue_condition;
352 std::deque<keypress> keypresses;
353 std::deque<std::string> commands;
354 std::deque<std::pair<void(*)(void*), void*>> functions;
355 volatile bool normal_pause;
356 volatile bool modal_pause;
357 volatile uint64_t continue_time;
358 volatile uint64_t next_function;
359 volatile uint64_t functions_executed;
361 void init_threading()
363 if(!queue_lock)
364 queue_lock = &mutex::aquire();
365 if(!queue_condition)
366 queue_condition = &condition::aquire(*queue_lock);
369 void internal_run_queues(bool unlocked) throw()
371 init_threading();
372 if(!unlocked)
373 queue_lock->lock();
374 try {
375 //Flush keypresses.
376 while(!keypresses.empty()) {
377 keypress k = keypresses.front();
378 keypresses.pop_front();
379 queue_lock->unlock();
380 if(k.key1)
381 k.key1->set_position(k.value, k.modifiers);
382 if(k.key2)
383 k.key2->set_position(k.value, k.modifiers);
384 queue_lock->lock();
386 //Flush commands.
387 while(!commands.empty()) {
388 std::string c = commands.front();
389 commands.pop_front();
390 queue_lock->unlock();
391 command::invokeC(c);
392 queue_lock->lock();
394 //Flush functions.
395 while(!functions.empty()) {
396 std::pair<void(*)(void*), void*> f = functions.front();
397 functions.pop_front();
398 queue_lock->unlock();
399 f.first(f.second);
400 queue_lock->lock();
401 ++functions_executed;
403 queue_condition->signal();
404 } catch(std::bad_alloc& e) {
405 OOM_panic();
406 } catch(std::exception& e) {
407 std::cerr << "Fault inside platform::run_queues(): " << e.what() << std::endl;
408 exit(1);
410 if(!unlocked)
411 queue_lock->unlock();
415 #define MAXWAIT 10000
417 void platform::flush_command_queue() throw()
419 if(modal_pause || normal_pause)
420 freeze_time(get_utime());
421 init_threading();
422 while(true) {
423 mutex::holder h(*queue_lock);
424 internal_run_queues(true);
425 if(!pausing_allowed)
426 break;
427 uint64_t now = get_utime();
428 uint64_t waitleft = 0;
429 waitleft = (now < continue_time) ? (continue_time - now) : 0;
430 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
431 waitleft = (waitleft > MAXWAIT) ? MAXWAIT : waitleft;
432 if(waitleft > 0)
433 queue_condition->wait(waitleft);
434 else
435 return;
436 //If we had to wait, check queues at least once more.
438 if(!modal_pause && !normal_pause)
439 unfreeze_time(get_utime());
442 void platform::set_paused(bool enable) throw()
444 normal_pause = enable;
447 void platform::wait(uint64_t usec) throw()
449 continue_time = get_utime() + usec;
450 init_threading();
451 while(true) {
452 mutex::holder h(*queue_lock);
453 internal_run_queues(true);
454 uint64_t now = get_utime();
455 uint64_t waitleft = 0;
456 waitleft = (now < continue_time) ? (continue_time - now) : 0;
457 waitleft = (waitleft > MAXWAIT) ? MAXWAIT : waitleft;
458 if(waitleft > 0)
459 queue_condition->wait(waitleft);
460 else
461 return;
465 void platform::cancel_wait() throw()
467 init_threading();
468 continue_time = 0;
469 mutex::holder h(*queue_lock);
470 queue_condition->signal();
473 void platform::set_modal_pause(bool enable) throw()
475 modal_pause = enable;
478 void platform::queue(const keypress& k) throw(std::bad_alloc)
480 init_threading();
481 mutex::holder h(*queue_lock);
482 keypresses.push_back(k);
483 queue_condition->signal();
486 void platform::queue(const std::string& c) throw(std::bad_alloc)
488 init_threading();
489 mutex::holder h(*queue_lock);
490 commands.push_back(c);
491 queue_condition->signal();
494 void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
496 if(sync && queue_synchronous_fn_warning)
497 std::cerr << "WARNING: Synchronous queue in callback to UI, this may deadlock!" << std::endl;
498 init_threading();
499 mutex::holder h(*queue_lock);
500 ++next_function;
501 functions.push_back(std::make_pair(f, arg));
502 queue_condition->signal();
503 if(sync)
504 while(functions_executed < next_function)
505 queue_condition->wait(10000);
508 void platform::run_queues() throw()
510 internal_run_queues(false);
513 namespace
515 mutex* _msgbuf_lock;
516 screen* our_screen;
518 void trigger_repaint()
520 graphics_plugin::notify_screen();
523 struct painter_listener : public information_dispatch
525 painter_listener();
526 void on_set_screen(screen& scr);
527 void on_screen_update();
528 void on_status_update();
529 } x;
531 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
533 void painter_listener::on_set_screen(screen& scr)
535 our_screen = &scr;
538 void painter_listener::on_screen_update()
540 trigger_repaint();
543 void painter_listener::on_status_update()
545 graphics_plugin::notify_status();
549 mutex& platform::msgbuf_lock() throw()
551 if(!_msgbuf_lock)
552 try {
553 _msgbuf_lock = &mutex::aquire();
554 } catch(...) {
555 OOM_panic();
557 return *_msgbuf_lock;
560 void platform::screen_set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
562 if(!our_screen)
563 return;
564 if(our_screen->palette_r == rshift &&
565 our_screen->palette_g == gshift &&
566 our_screen->palette_b == bshift)
567 return;
568 our_screen->set_palette(rshift, gshift, bshift);
569 trigger_repaint();
572 modal_pause_holder::modal_pause_holder()
574 platform::set_modal_pause(true);
577 modal_pause_holder::~modal_pause_holder()
579 platform::set_modal_pause(false);
582 bool platform::pausing_allowed = true;
583 volatile bool queue_synchronous_fn_warning;