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"
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"
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
36 volatile bool _system_thread_available
= false;
46 keypress::keypress(keyboard::modifier_set mod
, keyboard::key
& _key
, short _value
)
54 keypress::keypress(keyboard::modifier_set mod
, keyboard::key
& _key
, keyboard::key
& _key2
, short _value
)
62 volatile bool platform::do_exit_dummy_event_loop
= false;
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
;
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
)) {
81 if(!audioapi_driver_initialized())
82 throw std::runtime_error("Sound failed to initialize and is disabled");
83 platform::sound_enable(true);
86 if(audioapi_driver_initialized())
87 platform::sound_enable(false);
90 throw std::runtime_error("Bad sound setting");
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
;
102 typedef char char_type
;
103 typedef boost::iostreams::sink_tag category
;
104 window_output(int* dummy
)
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
);
118 size_t lf
= stream
.size();
119 for(size_t i
= 0; i
< stream
.size(); i
++)
120 if(stream
[i
] == '\n') {
124 if(lf
== stream
.size())
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);
135 std::vector
<char> stream
;
138 class msgcallback
: public messagebuffer::update_handler
141 ~msgcallback() throw() {};
142 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
144 platform::notify_message();
148 std::ofstream system_log
;
149 bool sounds_enabled
= true;
152 emulator_status
& platform::get_emustatus() throw()
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);
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.
174 audioapi_driver_set_device(old_play
, old_rec
);
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
);
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
;
201 graphics_driver_init();
203 audioapi_driver_init();
204 joystick_driver_init();
207 void platform::quit()
209 joystick_driver_quit();
210 audioapi_driver_quit();
212 graphics_driver_quit();
213 msgbuf
.unregister_handler(msg_callback_obj
);
214 time_t curtime
= time(NULL
);
215 struct tm
* tm
= localtime(&curtime
);
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
;
224 std::ostream
& platform::out() throw(std::bad_alloc
)
226 static std::ostream
* cached
= NULL
;
229 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
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
);
242 system_log
<< forlog
<< std::endl
;
246 void platform::fatal_error() throw()
248 time_t curtime
= time(NULL
);
249 struct tm
* tm
= localtime(&curtime
);
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
;
256 graphics_driver_fatal_error();
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()
284 while(!keypresses
.empty()) {
285 keypress k
= keypresses
.front();
286 keypresses
.pop_front();
289 k
.key1
->set_state(k
.modifiers
, k
.value
);
291 k
.key2
->set_state(k
.modifiers
, k
.value
);
293 queue_function_run
= true;
296 while(!commands
.empty()) {
297 std::string c
= commands
.front();
298 commands
.pop_front();
302 queue_function_run
= true;
305 while(!functions
.empty()) {
306 std::pair
<void(*)(void*), void*> f
= functions
.front();
307 functions
.pop_front();
311 ++functions_executed
;
312 queue_function_run
= true;
314 queue_condition
.notify_all();
315 } catch(std::bad_alloc
& e
) {
317 } catch(std::exception
& e
) {
318 std::cerr
<< "Fault inside platform::run_queues(): " << e
.what() << std::endl
;
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()
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()
351 do_exit_dummy_event_loop
= true;
352 umutex_class
h(queue_lock
);
353 queue_condition
.notify_all();
357 void platform::flush_command_queue() throw()
360 queue_function_run
= false;
361 if(modal_pause
|| normal_pause
)
362 freeze_time(get_utime());
364 bool run_idle
= false;
366 uint64_t now
= get_utime();
367 if(now
>= on_timer_time
) {
368 lua_callback_do_timer();
372 lua_callback_do_idle();
376 umutex_class
h(queue_lock
);
377 internal_run_queues(true);
380 if(queue_function_run
)
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
));
388 if(now
>= on_idle_time
) {
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
);
397 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
398 random_mix_timing_entropy();
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()
416 continue_time
= get_utime() + usec
;
418 bool run_idle
= false;
420 uint64_t now
= get_utime();
421 if(now
>= on_timer_time
) {
422 lua_callback_do_timer();
426 lua_callback_do_idle();
430 umutex_class
h(queue_lock
);
431 internal_run_queues(true);
432 if(queue_function_run
)
434 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
438 uint64_t waitleft
= 0;
439 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
440 waitleft
= min(static_cast<uint64_t>(MAXWAIT
), waitleft
);
442 if(now
>= on_idle_time
) {
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
);
451 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
452 random_mix_timing_entropy();
459 void platform::cancel_wait() throw()
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
)
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
)
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
) {
495 umutex_class
h(queue_lock
);
497 functions
.push_back(std::make_pair(f
, arg
));
498 queue_condition
.notify_all();
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
;
518 mutex_class _msgbuf_lock
;
519 framebuffer::fb
<false>* our_screen
;
521 struct 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
; });
530 struct dispatch::target
<> screenupdate
;
531 struct dispatch::target
<> statusupdate
;
532 struct dispatch::target
<framebuffer::fb
<false>&> setscreen
;
536 mutex_class
& platform::msgbuf_lock() throw()
541 void platform::screen_set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
545 if(our_screen
->get_palette_r() == rshift
&&
546 our_screen
->get_palette_g() == gshift
&&
547 our_screen
->get_palette_b() == bshift
)
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
;