1 #include "core/audioapi.hpp"
2 #include "core/command.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/framerate.hpp"
5 #include "core/instance.hpp"
6 #include "core/joystickapi.hpp"
7 #include "core/keymapper.hpp"
8 #include "core/messages.hpp"
9 #include "core/queue.hpp"
10 #include "core/random.hpp"
11 #include "core/window.hpp"
12 #include "fonts/wrapper.hpp"
13 #include "library/framebuffer.hpp"
14 #include "library/minmax.hpp"
15 #include "library/string.hpp"
16 #include "library/threads.hpp"
17 #include "lua/lua.hpp"
25 #include <boost/iostreams/categories.hpp>
26 #include <boost/iostreams/copy.hpp>
27 #include <boost/iostreams/stream.hpp>
28 #include <boost/iostreams/stream_buffer.hpp>
29 #include <boost/iostreams/filter/symmetric.hpp>
30 #include <boost/iostreams/filter/zlib.hpp>
31 #include <boost/iostreams/filtering_stream.hpp>
32 #include <boost/iostreams/device/back_inserter.hpp>
34 #define MAXMESSAGES 5000
35 #define INIT_WIN_SIZE 6
37 volatile bool platform::do_exit_dummy_event_loop
= false;
41 command::fnptr
<> identify_key(lsnes_cmds
, "show-plugins", "Show plugins in use",
42 "Syntax: show-plugins\nShows plugins in use.\n",
43 []() throw(std::bad_alloc
, std::runtime_error
) {
44 messages
<< "Graphics:\t" << graphics_driver_name() << std::endl
;
45 messages
<< "Sound:\t" << audioapi_driver_name() << std::endl
;
46 messages
<< "Joystick:\t" << joystick_driver_name() << std::endl
;
49 command::fnptr
<const std::string
&> enable_sound(lsnes_cmds
, "enable-sound", "Enable/Disable sound",
50 "Syntax: enable-sound <on/off/toggle>\nEnable or disable sound.\n",
51 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
52 if(args
== "toggle") {
53 if(!audioapi_driver_initialized())
54 throw std::runtime_error("Sound failed to initialize and is disabled");
55 platform::sound_enable(!platform::is_sound_enabled());
58 switch(string_to_bool(args
)) {
60 if(!audioapi_driver_initialized())
61 throw std::runtime_error("Sound failed to initialize and is disabled");
62 platform::sound_enable(true);
65 if(audioapi_driver_initialized())
66 platform::sound_enable(false);
69 throw std::runtime_error("Bad sound setting");
73 keyboard::invbind_info
ienable_sound(lsnes_invbinds
, "enable-sound on", "Sound‣Enable");
74 keyboard::invbind_info
idisable_sound(lsnes_invbinds
, "enable-sound off", "Sound‣Disable");
75 keyboard::invbind_info
itoggle_sound(lsnes_invbinds
, "enable-sound toggle", "Sound‣Toggle");
80 typedef char char_type
;
81 typedef boost::iostreams::sink_tag category
;
82 window_output(int* dummy
)
90 std::streamsize
write(const char* s
, std::streamsize n
)
92 size_t oldsize
= stream
.size();
93 stream
.resize(oldsize
+ n
);
94 memcpy(&stream
[oldsize
], s
, n
);
96 size_t lf
= stream
.size();
97 for(size_t i
= 0; i
< stream
.size(); i
++)
98 if(stream
[i
] == '\n') {
102 if(lf
== stream
.size())
104 std::string
foo(stream
.begin(), stream
.begin() + lf
);
105 platform::message(foo
);
106 if(lf
+ 1 < stream
.size())
107 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
108 stream
.resize(stream
.size() - lf
- 1);
113 std::vector
<char> stream
;
116 class msgcallback
: public messagebuffer::update_handler
119 ~msgcallback() throw() {};
120 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
122 platform::notify_message();
126 std::ofstream system_log
;
127 bool sounds_enabled
= true;
130 void platform::sound_enable(bool enable
) throw()
132 audioapi_driver_enable(enable
);
133 sounds_enabled
= enable
;
134 CORE().dispatch
->sound_unmute(enable
);
137 void platform::set_sound_device(const std::string
& pdev
, const std::string
& rdev
) throw()
139 std::string old_play
= audioapi_driver_get_device(false);
140 std::string old_rec
= audioapi_driver_get_device(true);
142 audioapi_driver_set_device(pdev
, rdev
);
143 } catch(std::exception
& e
) {
144 out() << "Error changing sound device: " << e
.what() << std::endl
;
145 //Try to restore the device.
147 audioapi_driver_set_device(old_play
, old_rec
);
151 //After failed change, we don't know what is selected.
152 CORE().dispatch
->sound_change(std::make_pair(audioapi_driver_get_device(true),
153 audioapi_driver_get_device(false)));
156 bool platform::is_sound_enabled() throw()
158 return sounds_enabled
;
162 void platform::init()
164 do_exit_dummy_event_loop
= false;
165 msgbuf
.register_handler(msg_callback_obj
);
166 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
167 time_t curtime
= time(NULL
);
168 struct tm
* tm
= localtime(&curtime
);
170 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
171 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
172 system_log
<< "lsnes started at " << buffer
<< std::endl
;
173 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
175 graphics_driver_init();
177 audioapi_driver_init();
178 joystick_driver_init();
181 void platform::quit()
183 joystick_driver_quit();
184 audioapi_driver_quit();
186 graphics_driver_quit();
187 msgbuf
.unregister_handler(msg_callback_obj
);
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 shutting down at " << buffer
<< std::endl
;
194 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
198 std::ostream
& platform::out() throw(std::bad_alloc
)
200 static std::ostream
* cached
= NULL
;
203 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
207 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
210 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
212 threads::alock
h(msgbuf_lock());
213 for(auto& forlog
: token_iterator_foreach(msg
, {"\n"})) {
214 msgbuf
.add_message(forlog
);
216 system_log
<< forlog
<< std::endl
;
220 void platform::fatal_error() throw()
222 time_t curtime
= time(NULL
);
223 struct tm
* tm
= localtime(&curtime
);
225 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
226 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
227 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
228 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
230 graphics_driver_fatal_error();
236 volatile bool normal_pause
;
237 volatile bool modal_pause
;
238 volatile uint64_t continue_time
;
241 uint64_t on_idle_time
;
242 uint64_t on_timer_time
;
243 void reload_lua_timers()
246 on_idle_time
= core
.lua2
->timed_hook(LUA_TIMED_HOOK_IDLE
);
247 on_timer_time
= core
.lua2
->timed_hook(LUA_TIMED_HOOK_TIMER
);
248 core
.iqueue
->queue_function_run
= false;
252 #define MAXWAIT 100000ULL
254 void platform::dummy_event_loop() throw()
257 while(!do_exit_dummy_event_loop
) {
258 threads::alock
h(core
.iqueue
->queue_lock
);
259 core
.iqueue
->run_queue(true);
260 threads::cv_timed_wait(core
.iqueue
->queue_condition
, h
, threads::ustime(MAXWAIT
));
261 random_mix_timing_entropy();
265 void platform::exit_dummy_event_loop() throw()
268 do_exit_dummy_event_loop
= true;
269 threads::alock
h(core
.iqueue
->queue_lock
);
270 core
.iqueue
->queue_condition
.notify_all();
274 void platform::flush_command_queue() throw()
278 core
.iqueue
->queue_function_run
= false;
279 if(modal_pause
|| normal_pause
)
280 core
.framerate
->freeze_time(framerate_regulator::get_utime());
281 bool run_idle
= false;
283 uint64_t now
= framerate_regulator::get_utime();
284 if(now
>= on_timer_time
) {
285 core
.lua2
->callback_do_timer();
289 core
.lua2
->callback_do_idle();
293 threads::alock
h(core
.iqueue
->queue_lock
);
294 core
.iqueue
->run_queue(true);
297 if(core
.iqueue
->queue_function_run
)
299 now
= framerate_regulator::get_utime();
300 uint64_t waitleft
= 0;
301 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
302 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
303 waitleft
= min(waitleft
, static_cast<uint64_t>(MAXWAIT
));
305 if(now
>= on_idle_time
) {
309 if(on_idle_time
>= now
)
310 waitleft
= min(waitleft
, on_idle_time
- now
);
311 if(on_timer_time
>= now
)
312 waitleft
= min(waitleft
, on_timer_time
- now
);
314 threads::cv_timed_wait(core
.iqueue
->queue_condition
, h
, threads::ustime(waitleft
));
315 random_mix_timing_entropy();
319 //If we had to wait, check queues at least once more.
321 if(!modal_pause
&& !normal_pause
)
322 core
.framerate
->unfreeze_time(framerate_regulator::get_utime());
325 void platform::set_paused(bool enable
) throw()
327 normal_pause
= enable
;
330 void platform::wait(uint64_t usec
) throw()
334 continue_time
= framerate_regulator::get_utime() + usec
;
335 bool run_idle
= false;
337 uint64_t now
= framerate_regulator::get_utime();
338 if(now
>= on_timer_time
) {
339 core
.lua2
->callback_do_timer();
343 core
.lua2
->callback_do_idle();
347 threads::alock
h(core
.iqueue
->queue_lock
);
348 core
.iqueue
->run_queue(true);
349 if(core
.iqueue
->queue_function_run
)
351 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
354 now
= framerate_regulator::get_utime();
355 uint64_t waitleft
= 0;
356 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
357 waitleft
= min(static_cast<uint64_t>(MAXWAIT
), waitleft
);
359 if(now
>= on_idle_time
) {
363 if(on_idle_time
>= now
)
364 waitleft
= min(waitleft
, on_idle_time
- now
);
365 if(on_timer_time
>= now
)
366 waitleft
= min(waitleft
, on_timer_time
- now
);
368 threads::cv_timed_wait(core
.iqueue
->queue_condition
, h
, threads::ustime(waitleft
));
369 random_mix_timing_entropy();
376 void platform::cancel_wait() throw()
380 threads::alock
h(core
.iqueue
->queue_lock
);
381 core
.iqueue
->queue_condition
.notify_all();
384 void platform::set_modal_pause(bool enable
) throw()
386 modal_pause
= enable
;
389 void platform::run_queues() throw()
391 CORE().iqueue
->run_queue(false);
396 threads::lock _msgbuf_lock
;
399 threads::lock
& platform::msgbuf_lock() throw()
404 modal_pause_holder::modal_pause_holder()
406 platform::set_modal_pause(true);
409 modal_pause_holder::~modal_pause_holder()
411 platform::set_modal_pause(false);
414 bool platform::pausing_allowed
= true;
415 double platform::global_volume
= 1.0;
416 volatile bool queue_synchronous_fn_warning
;