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
40 keypress::keypress(keyboard_modifier_set mod
, keyboard_key
& _key
, short _value
)
48 keypress::keypress(keyboard_modifier_set mod
, keyboard_key
& _key
, keyboard_key
& _key2
, short _value
)
56 volatile bool platform::do_exit_dummy_event_loop
= false;
60 bool queue_function_run
= false;
62 function_ptr_command
<> identify_key(lsnes_cmd
, "show-plugins", "Show plugins in use",
63 "Syntax: show-plugins\nShows plugins in use.\n",
64 []() throw(std::bad_alloc
, std::runtime_error
) {
65 messages
<< "Graphics:\t" << graphics_driver_name() << std::endl
;
66 messages
<< "Sound:\t" << audioapi_driver_name() << std::endl
;
67 messages
<< "Joystick:\t" << joystick_driver_name() << std::endl
;
70 function_ptr_command
<const std::string
&> enable_sound(lsnes_cmd
, "enable-sound", "Enable/Disable sound",
71 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
72 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
73 switch(string_to_bool(args
)) {
75 if(!audioapi_driver_initialized())
76 throw std::runtime_error("Sound failed to initialize and is disabled");
77 platform::sound_enable(true);
80 if(audioapi_driver_initialized())
81 platform::sound_enable(false);
84 throw std::runtime_error("Bad sound setting");
88 inverse_bind
ienable_sound(lsnes_mapper
, "enable-sound on", "Sound‣Enable");
89 inverse_bind
idisable_sound(lsnes_mapper
, "enable-sound off", "Sound‣Disable");
91 emulator_status emustatus
;
96 typedef char char_type
;
97 typedef boost::iostreams::sink_tag category
;
98 window_output(int* dummy
)
106 std::streamsize
write(const char* s
, std::streamsize n
)
108 size_t oldsize
= stream
.size();
109 stream
.resize(oldsize
+ n
);
110 memcpy(&stream
[oldsize
], s
, n
);
112 size_t lf
= stream
.size();
113 for(size_t i
= 0; i
< stream
.size(); i
++)
114 if(stream
[i
] == '\n') {
118 if(lf
== stream
.size())
120 std::string
foo(stream
.begin(), stream
.begin() + lf
);
121 platform::message(foo
);
122 if(lf
+ 1 < stream
.size())
123 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
124 stream
.resize(stream
.size() - lf
- 1);
129 std::vector
<char> stream
;
132 class msgcallback
: public messagebuffer::update_handler
135 ~msgcallback() throw() {};
136 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
138 platform::notify_message();
142 std::ofstream system_log
;
143 bool sounds_enabled
= true;
146 emulator_status
& platform::get_emustatus() throw()
151 void platform::sound_enable(bool enable
) throw()
153 audioapi_driver_enable(enable
);
154 sounds_enabled
= enable
;
155 information_dispatch::do_sound_unmute(enable
);
158 void platform::set_sound_device(const std::string
& pdev
, const std::string
& rdev
) throw()
160 std::string old_play
= audioapi_driver_get_device(false);
161 std::string old_rec
= audioapi_driver_get_device(true);
163 audioapi_driver_set_device(pdev
, rdev
);
164 } catch(std::exception
& e
) {
165 out() << "Error changing sound device: " << e
.what() << std::endl
;
166 //Try to restore the device.
168 audioapi_driver_set_device(old_play
, old_rec
);
172 //After failed change, we don't know what is selected.
173 information_dispatch::do_sound_change(std::make_pair(audioapi_driver_get_device(true),
174 audioapi_driver_get_device(false)));
177 bool platform::is_sound_enabled() throw()
179 return sounds_enabled
;
183 void platform::init()
185 do_exit_dummy_event_loop
= false;
186 msgbuf
.register_handler(msg_callback_obj
);
187 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
188 time_t curtime
= time(NULL
);
189 struct tm
* tm
= localtime(&curtime
);
191 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
192 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
193 system_log
<< "lsnes started at " << buffer
<< std::endl
;
194 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
196 graphics_driver_init();
198 audioapi_driver_init();
199 joystick_driver_init();
202 void platform::quit()
204 joystick_driver_quit();
205 audioapi_driver_quit();
207 graphics_driver_quit();
208 msgbuf
.unregister_handler(msg_callback_obj
);
209 time_t curtime
= time(NULL
);
210 struct tm
* tm
= localtime(&curtime
);
212 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
213 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
214 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
215 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
219 std::ostream
& platform::out() throw(std::bad_alloc
)
221 static std::ostream
* cached
= NULL
;
224 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
228 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
231 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
233 umutex_class
h(msgbuf_lock());
234 std::string msg2
= msg
;
237 extract_token(msg2
, forlog
, "\n");
238 msgbuf
.add_message(forlog
);
240 system_log
<< forlog
<< std::endl
;
244 void platform::fatal_error() throw()
246 time_t curtime
= time(NULL
);
247 struct tm
* tm
= localtime(&curtime
);
249 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
250 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
251 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
252 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
254 graphics_driver_fatal_error();
260 mutex_class queue_lock
;
261 cv_class queue_condition
;
262 std::deque
<keypress
> keypresses
;
263 std::deque
<std::string
> commands
;
264 std::deque
<std::pair
<void(*)(void*), void*>> functions
;
265 volatile bool normal_pause
;
266 volatile bool modal_pause
;
267 volatile uint64_t continue_time
;
268 volatile uint64_t next_function
;
269 volatile uint64_t functions_executed
;
271 void init_threading()
275 void internal_run_queues(bool unlocked
) throw()
282 while(!keypresses
.empty()) {
283 keypress k
= keypresses
.front();
284 keypresses
.pop_front();
287 k
.key1
->set_state(k
.modifiers
, k
.value
);
289 k
.key2
->set_state(k
.modifiers
, k
.value
);
291 queue_function_run
= true;
294 while(!commands
.empty()) {
295 std::string c
= commands
.front();
296 commands
.pop_front();
300 queue_function_run
= true;
303 while(!functions
.empty()) {
304 std::pair
<void(*)(void*), void*> f
= functions
.front();
305 functions
.pop_front();
309 ++functions_executed
;
310 queue_function_run
= true;
312 queue_condition
.notify_all();
313 } catch(std::bad_alloc
& e
) {
315 } catch(std::exception
& e
) {
316 std::cerr
<< "Fault inside platform::run_queues(): " << e
.what() << std::endl
;
323 uint64_t on_idle_time
;
324 uint64_t on_timer_time
;
325 void reload_lua_timers()
327 on_idle_time
= lua_timed_hook(LUA_TIMED_HOOK_IDLE
);
328 on_timer_time
= lua_timed_hook(LUA_TIMED_HOOK_TIMER
);
329 queue_function_run
= false;
333 #define MAXWAIT 100000ULL
335 void platform::dummy_event_loop() throw()
338 while(!do_exit_dummy_event_loop
) {
339 umutex_class
h(queue_lock
);
340 internal_run_queues(true);
341 cv_timed_wait(queue_condition
, h
, microsec_class(MAXWAIT
));
345 void platform::exit_dummy_event_loop() throw()
348 do_exit_dummy_event_loop
= true;
349 umutex_class
h(queue_lock
);
350 queue_condition
.notify_all();
354 void platform::flush_command_queue() throw()
357 queue_function_run
= false;
358 if(modal_pause
|| normal_pause
)
359 freeze_time(get_utime());
361 bool run_idle
= false;
363 uint64_t now
= get_utime();
364 if(now
>= on_timer_time
) {
365 lua_callback_do_timer();
369 lua_callback_do_idle();
373 umutex_class
h(queue_lock
);
374 internal_run_queues(true);
377 if(queue_function_run
)
380 uint64_t waitleft
= 0;
381 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
382 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
383 waitleft
= min(waitleft
, static_cast<uint64_t>(MAXWAIT
));
385 if(now
>= on_idle_time
) {
389 if(on_idle_time
>= now
)
390 waitleft
= min(waitleft
, on_idle_time
- now
);
391 if(on_timer_time
>= now
)
392 waitleft
= min(waitleft
, on_timer_time
- now
);
394 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
397 //If we had to wait, check queues at least once more.
399 if(!modal_pause
&& !normal_pause
)
400 unfreeze_time(get_utime());
403 void platform::set_paused(bool enable
) throw()
405 normal_pause
= enable
;
408 void platform::wait(uint64_t usec
) throw()
411 continue_time
= get_utime() + usec
;
413 bool run_idle
= false;
415 uint64_t now
= get_utime();
416 if(now
>= on_timer_time
) {
417 lua_callback_do_timer();
421 lua_callback_do_idle();
425 umutex_class
h(queue_lock
);
426 internal_run_queues(true);
427 if(queue_function_run
)
430 uint64_t waitleft
= 0;
431 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
432 waitleft
= min(static_cast<uint64_t>(MAXWAIT
), waitleft
);
434 if(now
>= on_idle_time
) {
438 if(on_idle_time
>= now
)
439 waitleft
= min(waitleft
, on_idle_time
- now
);
440 if(on_timer_time
>= now
)
441 waitleft
= min(waitleft
, on_timer_time
- now
);
443 cv_timed_wait(queue_condition
, h
, microsec_class(waitleft
));
449 void platform::cancel_wait() throw()
453 umutex_class
h(queue_lock
);
454 queue_condition
.notify_all();
457 void platform::set_modal_pause(bool enable
) throw()
459 modal_pause
= enable
;
462 void platform::queue(const keypress
& k
) throw(std::bad_alloc
)
465 umutex_class
h(queue_lock
);
466 keypresses
.push_back(k
);
467 queue_condition
.notify_all();
470 void platform::queue(const std::string
& c
) throw(std::bad_alloc
)
473 umutex_class
h(queue_lock
);
474 commands
.push_back(c
);
475 queue_condition
.notify_all();
478 void platform::queue(void (*f
)(void* arg
), void* arg
, bool sync
) throw(std::bad_alloc
)
481 umutex_class
h(queue_lock
);
483 functions
.push_back(std::make_pair(f
, arg
));
484 queue_condition
.notify_all();
486 while(functions_executed
< next_function
)
487 cv_timed_wait(queue_condition
, h
, microsec_class(10000));
490 void platform::run_queues() throw()
492 internal_run_queues(false);
497 mutex_class _msgbuf_lock
;
498 framebuffer
<false>* our_screen
;
500 struct painter_listener
: public information_dispatch
503 void on_set_screen(framebuffer
<false>& scr
);
504 void on_screen_update();
505 void on_status_update();
508 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
510 void painter_listener::on_set_screen(framebuffer
<false>& scr
)
515 void painter_listener::on_screen_update()
517 graphics_driver_notify_screen();
520 void painter_listener::on_status_update()
522 graphics_driver_notify_status();
526 mutex_class
& platform::msgbuf_lock() throw()
531 void platform::screen_set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
535 if(our_screen
->get_palette_r() == rshift
&&
536 our_screen
->get_palette_g() == gshift
&&
537 our_screen
->get_palette_b() == bshift
)
539 our_screen
->set_palette(rshift
, gshift
, bshift
);
540 graphics_driver_notify_screen();
543 modal_pause_holder::modal_pause_holder()
545 platform::set_modal_pause(true);
548 modal_pause_holder::~modal_pause_holder()
550 platform::set_modal_pause(false);
553 bool platform::pausing_allowed
= true;
554 double platform::global_volume
= 1.0;
555 volatile bool queue_synchronous_fn_warning
;