lsnes rr2-β15
[lsnes.git] / src / core / window.cpp
blob7b2ba4f5d5e7323d9e47478f8e6b5a2cd607697b
1 #include "core/audioapi.hpp"
2 #include "core/command.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/framerate.hpp"
5 #include "core/joystickapi.hpp"
6 #include "core/keymapper.hpp"
7 #include "lua/lua.hpp"
8 #include "core/misc.hpp"
9 #include "core/window.hpp"
10 #include "fonts/wrapper.hpp"
11 #include "library/framebuffer.hpp"
12 #include "library/string.hpp"
13 #include "library/minmax.hpp"
14 #include "library/threadtypes.hpp"
16 #include <fstream>
17 #include <iostream>
18 #include <string>
19 #include <deque>
20 #include <sys/time.h>
21 #include <unistd.h>
22 #include <boost/iostreams/categories.hpp>
23 #include <boost/iostreams/copy.hpp>
24 #include <boost/iostreams/stream.hpp>
25 #include <boost/iostreams/stream_buffer.hpp>
26 #include <boost/iostreams/filter/symmetric.hpp>
27 #include <boost/iostreams/filter/zlib.hpp>
28 #include <boost/iostreams/filtering_stream.hpp>
29 #include <boost/iostreams/device/back_inserter.hpp>
31 #define MAXMESSAGES 5000
32 #define INIT_WIN_SIZE 6
34 namespace
36 volatile bool _system_thread_available = false;
39 keypress::keypress()
41 key1 = NULL;
42 key2 = NULL;
43 value = 0;
46 keypress::keypress(keyboard::modifier_set mod, keyboard::key& _key, short _value)
48 modifiers = mod;
49 key1 = &_key;
50 key2 = NULL;
51 value = _value;
54 keypress::keypress(keyboard::modifier_set mod, keyboard::key& _key, keyboard::key& _key2, short _value)
56 modifiers = mod;
57 key1 = &_key;
58 key2 = &_key2;
59 value = _value;
62 volatile bool platform::do_exit_dummy_event_loop = false;
64 namespace
66 bool queue_function_run = false;
68 command::fnptr<> identify_key(lsnes_cmd, "show-plugins", "Show plugins in use",
69 "Syntax: show-plugins\nShows plugins in use.\n",
70 []() throw(std::bad_alloc, std::runtime_error) {
71 messages << "Graphics:\t" << graphics_driver_name() << std::endl;
72 messages << "Sound:\t" << audioapi_driver_name() << std::endl;
73 messages << "Joystick:\t" << joystick_driver_name() << std::endl;
74 });
76 command::fnptr<const std::string&> enable_sound(lsnes_cmd, "enable-sound", "Enable/Disable sound",
77 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
78 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
79 switch(string_to_bool(args)) {
80 case 1:
81 if(!audioapi_driver_initialized())
82 throw std::runtime_error("Sound failed to initialize and is disabled");
83 platform::sound_enable(true);
84 break;
85 case 0:
86 if(audioapi_driver_initialized())
87 platform::sound_enable(false);
88 break;
89 default:
90 throw std::runtime_error("Bad sound setting");
92 });
94 keyboard::invbind ienable_sound(lsnes_mapper, "enable-sound on", "Sound‣Enable");
95 keyboard::invbind idisable_sound(lsnes_mapper, "enable-sound off", "Sound‣Disable");
97 emulator_status emustatus;
99 class window_output
101 public:
102 typedef char char_type;
103 typedef boost::iostreams::sink_tag category;
104 window_output(int* dummy)
108 void close()
112 std::streamsize write(const char* s, std::streamsize n)
114 size_t oldsize = stream.size();
115 stream.resize(oldsize + n);
116 memcpy(&stream[oldsize], s, n);
117 while(true) {
118 size_t lf = stream.size();
119 for(size_t i = 0; i < stream.size(); i++)
120 if(stream[i] == '\n') {
121 lf = i;
122 break;
124 if(lf == stream.size())
125 break;
126 std::string foo(stream.begin(), stream.begin() + lf);
127 platform::message(foo);
128 if(lf + 1 < stream.size())
129 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
130 stream.resize(stream.size() - lf - 1);
132 return n;
134 protected:
135 std::vector<char> stream;
138 class msgcallback : public messagebuffer::update_handler
140 public:
141 ~msgcallback() throw() {};
142 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
144 platform::notify_message();
146 } msg_callback_obj;
148 std::ofstream system_log;
149 bool sounds_enabled = true;
152 emulator_status& platform::get_emustatus() throw()
154 return emustatus;
157 void platform::sound_enable(bool enable) throw()
159 audioapi_driver_enable(enable);
160 sounds_enabled = enable;
161 notify_sound_unmute(enable);
164 void platform::set_sound_device(const std::string& pdev, const std::string& rdev) throw()
166 std::string old_play = audioapi_driver_get_device(false);
167 std::string old_rec = audioapi_driver_get_device(true);
168 try {
169 audioapi_driver_set_device(pdev, rdev);
170 } catch(std::exception& e) {
171 out() << "Error changing sound device: " << e.what() << std::endl;
172 //Try to restore the device.
173 try {
174 audioapi_driver_set_device(old_play, old_rec);
175 } catch(...) {
178 //After failed change, we don't know what is selected.
179 notify_sound_change(std::make_pair(audioapi_driver_get_device(true), audioapi_driver_get_device(false)));
182 bool platform::is_sound_enabled() throw()
184 return sounds_enabled;
188 void platform::init()
190 do_exit_dummy_event_loop = false;
191 msgbuf.register_handler(msg_callback_obj);
192 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
193 time_t curtime = time(NULL);
194 struct tm* tm = localtime(&curtime);
195 char buffer[1024];
196 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
197 system_log << "-----------------------------------------------------------------------" << std::endl;
198 system_log << "lsnes started at " << buffer << std::endl;
199 system_log << "-----------------------------------------------------------------------" << std::endl;
200 do_init_font();
201 graphics_driver_init();
202 audioapi_init();
203 audioapi_driver_init();
204 joystick_driver_init();
207 void platform::quit()
209 joystick_driver_quit();
210 audioapi_driver_quit();
211 audioapi_quit();
212 graphics_driver_quit();
213 msgbuf.unregister_handler(msg_callback_obj);
214 time_t curtime = time(NULL);
215 struct tm* tm = localtime(&curtime);
216 char buffer[1024];
217 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
218 system_log << "-----------------------------------------------------------------------" << std::endl;
219 system_log << "lsnes shutting down at " << buffer << std::endl;
220 system_log << "-----------------------------------------------------------------------" << std::endl;
221 system_log.close();
224 std::ostream& platform::out() throw(std::bad_alloc)
226 static std::ostream* cached = NULL;
227 int dummy;
228 if(!cached)
229 cached = new boost::iostreams::stream<window_output>(&dummy);
230 return *cached;
233 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
236 void platform::message(const std::string& msg) throw(std::bad_alloc)
238 umutex_class h(msgbuf_lock());
239 for(auto& forlog : token_iterator_foreach(msg, {"\n"})) {
240 msgbuf.add_message(forlog);
241 if(system_log)
242 system_log << forlog << std::endl;
246 void platform::fatal_error() throw()
248 time_t curtime = time(NULL);
249 struct tm* tm = localtime(&curtime);
250 char buffer[1024];
251 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
252 system_log << "-----------------------------------------------------------------------" << std::endl;
253 system_log << "lsnes paniced at " << buffer << std::endl;
254 system_log << "-----------------------------------------------------------------------" << std::endl;
255 system_log.close();
256 graphics_driver_fatal_error();
257 exit(1);
260 namespace
262 mutex_class queue_lock;
263 cv_class queue_condition;
264 std::deque<keypress> keypresses;
265 std::deque<std::string> commands;
266 std::deque<std::pair<void(*)(void*), void*>> functions;
267 volatile bool normal_pause;
268 volatile bool modal_pause;
269 volatile uint64_t continue_time;
270 volatile uint64_t next_function;
271 volatile uint64_t functions_executed;
273 void init_threading()
277 void internal_run_queues(bool unlocked) throw()
279 init_threading();
280 if(!unlocked)
281 queue_lock.lock();
282 try {
283 //Flush keypresses.
284 while(!keypresses.empty()) {
285 keypress k = keypresses.front();
286 keypresses.pop_front();
287 queue_lock.unlock();
288 if(k.key1)
289 k.key1->set_state(k.modifiers, k.value);
290 if(k.key2)
291 k.key2->set_state(k.modifiers, k.value);
292 queue_lock.lock();
293 queue_function_run = true;
295 //Flush commands.
296 while(!commands.empty()) {
297 std::string c = commands.front();
298 commands.pop_front();
299 queue_lock.unlock();
300 lsnes_cmd.invoke(c);
301 queue_lock.lock();
302 queue_function_run = true;
304 //Flush functions.
305 while(!functions.empty()) {
306 std::pair<void(*)(void*), void*> f = functions.front();
307 functions.pop_front();
308 queue_lock.unlock();
309 f.first(f.second);
310 queue_lock.lock();
311 ++functions_executed;
312 queue_function_run = true;
314 queue_condition.notify_all();
315 } catch(std::bad_alloc& e) {
316 OOM_panic();
317 } catch(std::exception& e) {
318 std::cerr << "Fault inside platform::run_queues(): " << e.what() << std::endl;
319 exit(1);
321 if(!unlocked)
322 queue_lock.unlock();
325 uint64_t on_idle_time;
326 uint64_t on_timer_time;
327 void reload_lua_timers()
329 on_idle_time = lua_timed_hook(LUA_TIMED_HOOK_IDLE);
330 on_timer_time = lua_timed_hook(LUA_TIMED_HOOK_TIMER);
331 queue_function_run = false;
335 #define MAXWAIT 100000ULL
337 void platform::dummy_event_loop() throw()
339 init_threading();
340 while(!do_exit_dummy_event_loop) {
341 umutex_class h(queue_lock);
342 internal_run_queues(true);
343 cv_timed_wait(queue_condition, h, microsec_class(MAXWAIT));
344 random_mix_timing_entropy();
348 void platform::exit_dummy_event_loop() throw()
350 init_threading();
351 do_exit_dummy_event_loop = true;
352 umutex_class h(queue_lock);
353 queue_condition.notify_all();
354 usleep(200000);
357 void platform::flush_command_queue() throw()
359 reload_lua_timers();
360 queue_function_run = false;
361 if(modal_pause || normal_pause)
362 freeze_time(get_utime());
363 init_threading();
364 bool run_idle = false;
365 while(true) {
366 uint64_t now = get_utime();
367 if(now >= on_timer_time) {
368 lua_callback_do_timer();
369 reload_lua_timers();
371 if(run_idle) {
372 lua_callback_do_idle();
373 reload_lua_timers();
374 run_idle = false;
376 umutex_class h(queue_lock);
377 internal_run_queues(true);
378 if(!pausing_allowed)
379 break;
380 if(queue_function_run)
381 reload_lua_timers();
382 now = get_utime();
383 uint64_t waitleft = 0;
384 waitleft = (now < continue_time) ? (continue_time - now) : 0;
385 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
386 waitleft = min(waitleft, static_cast<uint64_t>(MAXWAIT));
387 if(waitleft > 0) {
388 if(now >= on_idle_time) {
389 run_idle = true;
390 waitleft = 0;
392 if(on_idle_time >= now)
393 waitleft = min(waitleft, on_idle_time - now);
394 if(on_timer_time >= now)
395 waitleft = min(waitleft, on_timer_time - now);
396 if(waitleft > 0) {
397 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
398 random_mix_timing_entropy();
400 } else
401 break;
402 //If we had to wait, check queues at least once more.
404 if(!modal_pause && !normal_pause)
405 unfreeze_time(get_utime());
408 void platform::set_paused(bool enable) throw()
410 normal_pause = enable;
413 void platform::wait(uint64_t usec) throw()
415 reload_lua_timers();
416 continue_time = get_utime() + usec;
417 init_threading();
418 bool run_idle = false;
419 while(true) {
420 uint64_t now = get_utime();
421 if(now >= on_timer_time) {
422 lua_callback_do_timer();
423 reload_lua_timers();
425 if(run_idle) {
426 lua_callback_do_idle();
427 run_idle = false;
428 reload_lua_timers();
430 umutex_class h(queue_lock);
431 internal_run_queues(true);
432 if(queue_function_run)
433 reload_lua_timers();
434 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
435 if(!usec)
436 return;
437 now = get_utime();
438 uint64_t waitleft = 0;
439 waitleft = (now < continue_time) ? (continue_time - now) : 0;
440 waitleft = min(static_cast<uint64_t>(MAXWAIT), waitleft);
441 if(waitleft > 0) {
442 if(now >= on_idle_time) {
443 run_idle = true;
444 waitleft = 0;
446 if(on_idle_time >= now)
447 waitleft = min(waitleft, on_idle_time - now);
448 if(on_timer_time >= now)
449 waitleft = min(waitleft, on_timer_time - now);
450 if(waitleft > 0) {
451 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
452 random_mix_timing_entropy();
454 } else
455 return;
459 void platform::cancel_wait() throw()
461 init_threading();
462 continue_time = 0;
463 umutex_class h(queue_lock);
464 queue_condition.notify_all();
467 void platform::set_modal_pause(bool enable) throw()
469 modal_pause = enable;
472 void platform::queue(const keypress& k) throw(std::bad_alloc)
474 init_threading();
475 umutex_class h(queue_lock);
476 keypresses.push_back(k);
477 queue_condition.notify_all();
480 void platform::queue(const std::string& c) throw(std::bad_alloc)
482 init_threading();
483 umutex_class h(queue_lock);
484 commands.push_back(c);
485 queue_condition.notify_all();
488 void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
490 if(!_system_thread_available) {
491 f(arg);
492 return;
494 init_threading();
495 umutex_class h(queue_lock);
496 ++next_function;
497 functions.push_back(std::make_pair(f, arg));
498 queue_condition.notify_all();
499 if(sync)
500 while(functions_executed < next_function && _system_thread_available) {
501 cv_timed_wait(queue_condition, h, microsec_class(10000));
502 random_mix_timing_entropy();
506 void platform::run_queues() throw()
508 internal_run_queues(false);
511 void platform::system_thread_available(bool av) throw()
513 _system_thread_available = av;
516 namespace
518 mutex_class _msgbuf_lock;
519 framebuffer::fb<false>* our_screen;
521 struct painter_listener
523 painter_listener()
525 screenupdate.set(notify_screen_update, []() { graphics_driver_notify_screen(); });
526 statusupdate.set(notify_status_update, []() { graphics_driver_notify_status(); });
527 setscreen.set(notify_set_screen, [](framebuffer::fb<false>& scr) { our_screen = &scr; });
529 private:
530 struct dispatch::target<> screenupdate;
531 struct dispatch::target<> statusupdate;
532 struct dispatch::target<framebuffer::fb<false>&> setscreen;
533 } x;
536 mutex_class& platform::msgbuf_lock() throw()
538 return _msgbuf_lock;
541 void platform::screen_set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
543 if(!our_screen)
544 return;
545 if(our_screen->get_palette_r() == rshift &&
546 our_screen->get_palette_g() == gshift &&
547 our_screen->get_palette_b() == bshift)
548 return;
549 our_screen->set_palette(rshift, gshift, bshift);
550 graphics_driver_notify_screen();
553 modal_pause_holder::modal_pause_holder()
555 platform::set_modal_pause(true);
558 modal_pause_holder::~modal_pause_holder()
560 platform::set_modal_pause(false);
563 bool platform::pausing_allowed = true;
564 double platform::global_volume = 1.0;
565 volatile bool queue_synchronous_fn_warning;