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"
7 #include "core/misc.hpp"
8 #include "core/window.hpp"
9 #include "fonts/wrapper.hpp"
10 #include "library/framebuffer.hpp"
11 #include "library/string.hpp"
12 #include "library/minmax.hpp"
13 #include "library/threadtypes.hpp"
21 #include <boost/iostreams/categories.hpp>
22 #include <boost/iostreams/copy.hpp>
23 #include <boost/iostreams/stream.hpp>
24 #include <boost/iostreams/stream_buffer.hpp>
25 #include <boost/iostreams/filter/symmetric.hpp>
26 #include <boost/iostreams/filter/zlib.hpp>
27 #include <boost/iostreams/filtering_stream.hpp>
28 #include <boost/iostreams/device/back_inserter.hpp>
30 #define MAXMESSAGES 5000
31 #define INIT_WIN_SIZE 6
35 volatile bool _system_thread_available
= false;
45 keypress::keypress(keyboard_modifier_set mod
, keyboard_key
& _key
, short _value
)
53 keypress::keypress(keyboard_modifier_set mod
, keyboard_key
& _key
, keyboard_key
& _key2
, short _value
)
61 volatile bool platform::do_exit_dummy_event_loop
= false;
65 bool queue_function_run
= false;
67 function_ptr_command
<> identify_key(lsnes_cmd
, "show-plugins", "Show plugins in use",
68 "Syntax: show-plugins\nShows plugins in use.\n",
69 []() throw(std::bad_alloc
, std::runtime_error
) {
70 messages
<< "Graphics:\t" << graphics_driver_name() << std::endl
;
71 messages
<< "Sound:\t" << audioapi_driver_name() << std::endl
;
72 messages
<< "Joystick:\t" << joystick_driver_name() << std::endl
;
75 function_ptr_command
<const std::string
&> enable_sound(lsnes_cmd
, "enable-sound", "Enable/Disable sound",
76 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
77 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
78 switch(string_to_bool(args
)) {
80 if(!audioapi_driver_initialized())
81 throw std::runtime_error("Sound failed to initialize and is disabled");
82 platform::sound_enable(true);
85 if(audioapi_driver_initialized())
86 platform::sound_enable(false);
89 throw std::runtime_error("Bad sound setting");
93 inverse_bind
ienable_sound(lsnes_mapper
, "enable-sound on", "Sound‣Enable");
94 inverse_bind
idisable_sound(lsnes_mapper
, "enable-sound off", "Sound‣Disable");
96 emulator_status emustatus
;
101 typedef char char_type
;
102 typedef boost::iostreams::sink_tag category
;
103 window_output(int* dummy
)
111 std::streamsize
write(const char* s
, std::streamsize n
)
113 size_t oldsize
= stream
.size();
114 stream
.resize(oldsize
+ n
);
115 memcpy(&stream
[oldsize
], s
, n
);
117 size_t lf
= stream
.size();
118 for(size_t i
= 0; i
< stream
.size(); i
++)
119 if(stream
[i
] == '\n') {
123 if(lf
== stream
.size())
125 std::string
foo(stream
.begin(), stream
.begin() + lf
);
126 platform::message(foo
);
127 if(lf
+ 1 < stream
.size())
128 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
129 stream
.resize(stream
.size() - lf
- 1);
134 std::vector
<char> stream
;
137 class msgcallback
: public messagebuffer::update_handler
140 ~msgcallback() throw() {};
141 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
143 platform::notify_message();
147 std::ofstream system_log
;
148 bool sounds_enabled
= true;
151 emulator_status
& platform::get_emustatus() throw()
156 void platform::sound_enable(bool enable
) throw()
158 audioapi_driver_enable(enable
);
159 sounds_enabled
= enable
;
160 notify_sound_unmute(enable
);
163 void platform::set_sound_device(const std::string
& pdev
, const std::string
& rdev
) throw()
165 std::string old_play
= audioapi_driver_get_device(false);
166 std::string old_rec
= audioapi_driver_get_device(true);
168 audioapi_driver_set_device(pdev
, rdev
);
169 } catch(std::exception
& e
) {
170 out() << "Error changing sound device: " << e
.what() << std::endl
;
171 //Try to restore the device.
173 audioapi_driver_set_device(old_play
, old_rec
);
177 //After failed change, we don't know what is selected.
178 notify_sound_change(std::make_pair(audioapi_driver_get_device(true), audioapi_driver_get_device(false)));
181 bool platform::is_sound_enabled() throw()
183 return sounds_enabled
;
187 void platform::init()
189 do_exit_dummy_event_loop
= false;
190 msgbuf
.register_handler(msg_callback_obj
);
191 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
192 time_t curtime
= time(NULL
);
193 struct tm
* tm
= localtime(&curtime
);
195 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
196 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
197 system_log
<< "lsnes started at " << buffer
<< std::endl
;
198 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
200 graphics_driver_init();
202 audioapi_driver_init();
203 joystick_driver_init();
206 void platform::quit()
208 joystick_driver_quit();
209 audioapi_driver_quit();
211 graphics_driver_quit();
212 msgbuf
.unregister_handler(msg_callback_obj
);
213 time_t curtime
= time(NULL
);
214 struct tm
* tm
= localtime(&curtime
);
216 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
217 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
218 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
219 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
223 std::ostream
& platform::out() throw(std::bad_alloc
)
225 static std::ostream
* cached
= NULL
;
228 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
232 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
235 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
237 umutex_class
h(msgbuf_lock());
238 std::string msg2
= msg
;
241 extract_token(msg2
, forlog
, "\n");
242 msgbuf
.add_message(forlog
);
244 system_log
<< forlog
<< std::endl
;
248 void platform::fatal_error() throw()
250 time_t curtime
= time(NULL
);
251 struct tm
* tm
= localtime(&curtime
);
253 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
254 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
255 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
256 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
258 graphics_driver_fatal_error();
264 mutex_class queue_lock
;
265 cv_class queue_condition
;
266 std::deque
<keypress
> keypresses
;
267 std::deque
<std::string
> commands
;
268 std::deque
<std::pair
<void(*)(void*), void*>> functions
;
269 volatile bool normal_pause
;
270 volatile bool modal_pause
;
271 volatile uint64_t continue_time
;
272 volatile uint64_t next_function
;
273 volatile uint64_t functions_executed
;
275 void init_threading()
279 void internal_run_queues(bool unlocked
) throw()
286 while(!keypresses
.empty()) {
287 keypress k
= keypresses
.front();
288 keypresses
.pop_front();
291 k
.key1
->set_state(k
.modifiers
, k
.value
);
293 k
.key2
->set_state(k
.modifiers
, k
.value
);
295 queue_function_run
= true;
298 while(!commands
.empty()) {
299 std::string c
= commands
.front();
300 commands
.pop_front();
304 queue_function_run
= true;
307 while(!functions
.empty()) {
308 std::pair
<void(*)(void*), void*> f
= functions
.front();
309 functions
.pop_front();
313 ++functions_executed
;
314 queue_function_run
= true;
316 queue_condition
.notify_all();
317 } catch(std::bad_alloc
& e
) {
319 } catch(std::exception
& e
) {
320 std::cerr
<< "Fault inside platform::run_queues(): " << e
.what() << std::endl
;
327 uint64_t on_idle_time
;
328 uint64_t on_timer_time
;
329 void reload_lua_timers()
331 on_idle_time
= lua_timed_hook(LUA_TIMED_HOOK_IDLE
);
332 on_timer_time
= lua_timed_hook(LUA_TIMED_HOOK_TIMER
);
333 queue_function_run
= false;
337 #define MAXWAIT 100000ULL
339 void platform::dummy_event_loop() throw()
342 while(!do_exit_dummy_event_loop
) {
343 umutex_class
h(queue_lock
);
344 internal_run_queues(true);
345 cv_timed_wait(queue_condition
, h
, microsec_class(MAXWAIT
));
349 void platform::exit_dummy_event_loop() throw()
352 do_exit_dummy_event_loop
= true;
353 umutex_class
h(queue_lock
);
354 queue_condition
.notify_all();
358 void platform::flush_command_queue() throw()
361 queue_function_run
= false;
362 if(modal_pause
|| normal_pause
)
363 freeze_time(get_utime());
365 bool run_idle
= false;
367 uint64_t now
= get_utime();
368 if(now
>= on_timer_time
) {
369 lua_callback_do_timer();
373 lua_callback_do_idle();
377 umutex_class
h(queue_lock
);
378 internal_run_queues(true);
381 if(queue_function_run
)
384 uint64_t waitleft
= 0;
385 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
386 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
387 waitleft
= min(waitleft
, static_cast<uint64_t>(MAXWAIT
));
389 if(now
>= on_idle_time
) {
393 if(on_idle_time
>= now
)
394 waitleft
= min(waitleft
, on_idle_time
- now
);
395 if(on_timer_time
>= now
)
396 waitleft
= min(waitleft
, on_timer_time
- now
);
398 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
401 //If we had to wait, check queues at least once more.
403 if(!modal_pause
&& !normal_pause
)
404 unfreeze_time(get_utime());
407 void platform::set_paused(bool enable
) throw()
409 normal_pause
= enable
;
412 void platform::wait(uint64_t usec
) throw()
415 continue_time
= get_utime() + usec
;
417 bool run_idle
= false;
419 uint64_t now
= get_utime();
420 if(now
>= on_timer_time
) {
421 lua_callback_do_timer();
425 lua_callback_do_idle();
429 umutex_class
h(queue_lock
);
430 internal_run_queues(true);
431 if(queue_function_run
)
434 uint64_t waitleft
= 0;
435 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
436 waitleft
= min(static_cast<uint64_t>(MAXWAIT
), waitleft
);
438 if(now
>= on_idle_time
) {
442 if(on_idle_time
>= now
)
443 waitleft
= min(waitleft
, on_idle_time
- now
);
444 if(on_timer_time
>= now
)
445 waitleft
= min(waitleft
, on_timer_time
- now
);
447 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
453 void platform::cancel_wait() throw()
457 umutex_class
h(queue_lock
);
458 queue_condition
.notify_all();
461 void platform::set_modal_pause(bool enable
) throw()
463 modal_pause
= enable
;
466 void platform::queue(const keypress
& k
) throw(std::bad_alloc
)
469 umutex_class
h(queue_lock
);
470 keypresses
.push_back(k
);
471 queue_condition
.notify_all();
474 void platform::queue(const std::string
& c
) throw(std::bad_alloc
)
477 umutex_class
h(queue_lock
);
478 commands
.push_back(c
);
479 queue_condition
.notify_all();
482 void platform::queue(void (*f
)(void* arg
), void* arg
, bool sync
) throw(std::bad_alloc
)
484 if(!_system_thread_available
) {
489 umutex_class
h(queue_lock
);
491 functions
.push_back(std::make_pair(f
, arg
));
492 queue_condition
.notify_all();
494 while(functions_executed
< next_function
&& _system_thread_available
)
495 cv_timed_wait(queue_condition
, h
, microsec_class(10000));
498 void platform::run_queues() throw()
500 internal_run_queues(false);
503 void platform::system_thread_available(bool av
) throw()
505 _system_thread_available
= av
;
510 mutex_class _msgbuf_lock
;
511 framebuffer
<false>* our_screen
;
513 struct painter_listener
517 screenupdate
.set(notify_screen_update
, []() { graphics_driver_notify_screen(); });
518 statusupdate
.set(notify_status_update
, []() { graphics_driver_notify_status(); });
519 setscreen
.set(notify_set_screen
, [](framebuffer
<false>& scr
) { our_screen
= &scr
; });
522 struct dispatch_target
<> screenupdate
;
523 struct dispatch_target
<> statusupdate
;
524 struct dispatch_target
<framebuffer
<false>&> setscreen
;
528 mutex_class
& platform::msgbuf_lock() throw()
533 void platform::screen_set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
537 if(our_screen
->get_palette_r() == rshift
&&
538 our_screen
->get_palette_g() == gshift
&&
539 our_screen
->get_palette_b() == bshift
)
541 our_screen
->set_palette(rshift
, gshift
, bshift
);
542 graphics_driver_notify_screen();
545 modal_pause_holder::modal_pause_holder()
547 platform::set_modal_pause(true);
550 modal_pause_holder::~modal_pause_holder()
552 platform::set_modal_pause(false);
555 bool platform::pausing_allowed
= true;
556 double platform::global_volume
= 1.0;
557 volatile bool queue_synchronous_fn_warning
;