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"
9 #include "core/misc.hpp"
10 #include "core/random.hpp"
11 #include "core/window.hpp"
12 #include "fonts/wrapper.hpp"
13 #include "library/framebuffer.hpp"
14 #include "library/string.hpp"
15 #include "library/minmax.hpp"
16 #include "library/threads.hpp"
24 #include <boost/iostreams/categories.hpp>
25 #include <boost/iostreams/copy.hpp>
26 #include <boost/iostreams/stream.hpp>
27 #include <boost/iostreams/stream_buffer.hpp>
28 #include <boost/iostreams/filter/symmetric.hpp>
29 #include <boost/iostreams/filter/zlib.hpp>
30 #include <boost/iostreams/filtering_stream.hpp>
31 #include <boost/iostreams/device/back_inserter.hpp>
33 #define MAXMESSAGES 5000
34 #define INIT_WIN_SIZE 6
36 volatile bool platform::do_exit_dummy_event_loop
= false;
40 command::fnptr
<> identify_key(lsnes_cmds
, "show-plugins", "Show plugins in use",
41 "Syntax: show-plugins\nShows plugins in use.\n",
42 []() throw(std::bad_alloc
, std::runtime_error
) {
43 messages
<< "Graphics:\t" << graphics_driver_name() << std::endl
;
44 messages
<< "Sound:\t" << audioapi_driver_name() << std::endl
;
45 messages
<< "Joystick:\t" << joystick_driver_name() << std::endl
;
48 command::fnptr
<const std::string
&> enable_sound(lsnes_cmds
, "enable-sound", "Enable/Disable sound",
49 "Syntax: enable-sound <on/off/toggle>\nEnable or disable sound.\n",
50 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
51 if(args
== "toggle") {
52 if(!audioapi_driver_initialized())
53 throw std::runtime_error("Sound failed to initialize and is disabled");
54 platform::sound_enable(!platform::is_sound_enabled());
57 switch(string_to_bool(args
)) {
59 if(!audioapi_driver_initialized())
60 throw std::runtime_error("Sound failed to initialize and is disabled");
61 platform::sound_enable(true);
64 if(audioapi_driver_initialized())
65 platform::sound_enable(false);
68 throw std::runtime_error("Bad sound setting");
72 keyboard::invbind_info
ienable_sound(lsnes_invbinds
, "enable-sound on", "Sound‣Enable");
73 keyboard::invbind_info
idisable_sound(lsnes_invbinds
, "enable-sound off", "Sound‣Disable");
74 keyboard::invbind_info
itoggle_sound(lsnes_invbinds
, "enable-sound toggle", "Sound‣Toggle");
79 typedef char char_type
;
80 typedef boost::iostreams::sink_tag category
;
81 window_output(int* dummy
)
89 std::streamsize
write(const char* s
, std::streamsize n
)
91 size_t oldsize
= stream
.size();
92 stream
.resize(oldsize
+ n
);
93 memcpy(&stream
[oldsize
], s
, n
);
95 size_t lf
= stream
.size();
96 for(size_t i
= 0; i
< stream
.size(); i
++)
97 if(stream
[i
] == '\n') {
101 if(lf
== stream
.size())
103 std::string
foo(stream
.begin(), stream
.begin() + lf
);
104 platform::message(foo
);
105 if(lf
+ 1 < stream
.size())
106 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
107 stream
.resize(stream
.size() - lf
- 1);
112 std::vector
<char> stream
;
115 class msgcallback
: public messagebuffer::update_handler
118 ~msgcallback() throw() {};
119 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
121 platform::notify_message();
125 std::ofstream system_log
;
126 bool sounds_enabled
= true;
129 void platform::sound_enable(bool enable
) throw()
131 audioapi_driver_enable(enable
);
132 sounds_enabled
= enable
;
133 notify_sound_unmute(enable
);
136 void platform::set_sound_device(const std::string
& pdev
, const std::string
& rdev
) throw()
138 std::string old_play
= audioapi_driver_get_device(false);
139 std::string old_rec
= audioapi_driver_get_device(true);
141 audioapi_driver_set_device(pdev
, rdev
);
142 } catch(std::exception
& e
) {
143 out() << "Error changing sound device: " << e
.what() << std::endl
;
144 //Try to restore the device.
146 audioapi_driver_set_device(old_play
, old_rec
);
150 //After failed change, we don't know what is selected.
151 notify_sound_change(std::make_pair(audioapi_driver_get_device(true), audioapi_driver_get_device(false)));
154 bool platform::is_sound_enabled() throw()
156 return sounds_enabled
;
160 void platform::init()
162 do_exit_dummy_event_loop
= false;
163 msgbuf
.register_handler(msg_callback_obj
);
164 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
165 time_t curtime
= time(NULL
);
166 struct tm
* tm
= localtime(&curtime
);
168 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
169 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
170 system_log
<< "lsnes started at " << buffer
<< std::endl
;
171 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
173 graphics_driver_init();
175 audioapi_driver_init();
176 joystick_driver_init();
179 void platform::quit()
181 joystick_driver_quit();
182 audioapi_driver_quit();
184 graphics_driver_quit();
185 msgbuf
.unregister_handler(msg_callback_obj
);
186 time_t curtime
= time(NULL
);
187 struct tm
* tm
= localtime(&curtime
);
189 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
190 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
191 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
192 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
196 std::ostream
& platform::out() throw(std::bad_alloc
)
198 static std::ostream
* cached
= NULL
;
201 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
205 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
208 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
210 threads::alock
h(msgbuf_lock());
211 for(auto& forlog
: token_iterator_foreach(msg
, {"\n"})) {
212 msgbuf
.add_message(forlog
);
214 system_log
<< forlog
<< std::endl
;
218 void platform::fatal_error() throw()
220 time_t curtime
= time(NULL
);
221 struct tm
* tm
= localtime(&curtime
);
223 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
224 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
225 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
226 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
228 graphics_driver_fatal_error();
234 volatile bool normal_pause
;
235 volatile bool modal_pause
;
236 volatile uint64_t continue_time
;
239 uint64_t on_idle_time
;
240 uint64_t on_timer_time
;
241 void reload_lua_timers()
243 on_idle_time
= lua_timed_hook(LUA_TIMED_HOOK_IDLE
);
244 on_timer_time
= lua_timed_hook(LUA_TIMED_HOOK_TIMER
);
245 CORE().queue_function_run
= false;
249 #define MAXWAIT 100000ULL
251 void platform::dummy_event_loop() throw()
253 while(!do_exit_dummy_event_loop
) {
254 threads::alock
h(CORE().queue_lock
);
255 CORE().run_queue(true);
256 threads::cv_timed_wait(CORE().queue_condition
, h
, threads::ustime(MAXWAIT
));
257 random_mix_timing_entropy();
261 void platform::exit_dummy_event_loop() throw()
263 do_exit_dummy_event_loop
= true;
264 threads::alock
h(CORE().queue_lock
);
265 CORE().queue_condition
.notify_all();
269 void platform::flush_command_queue() throw()
272 CORE().queue_function_run
= false;
273 if(modal_pause
|| normal_pause
)
274 CORE().framerate
.freeze_time(framerate_regulator::get_utime());
275 bool run_idle
= false;
277 uint64_t now
= framerate_regulator::get_utime();
278 if(now
>= on_timer_time
) {
279 lua_callback_do_timer();
283 lua_callback_do_idle();
287 threads::alock
h(CORE().queue_lock
);
288 CORE().run_queue(true);
291 if(CORE().queue_function_run
)
293 now
= framerate_regulator::get_utime();
294 uint64_t waitleft
= 0;
295 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
296 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
297 waitleft
= min(waitleft
, static_cast<uint64_t>(MAXWAIT
));
299 if(now
>= on_idle_time
) {
303 if(on_idle_time
>= now
)
304 waitleft
= min(waitleft
, on_idle_time
- now
);
305 if(on_timer_time
>= now
)
306 waitleft
= min(waitleft
, on_timer_time
- now
);
308 threads::cv_timed_wait(CORE().queue_condition
, h
, threads::ustime(waitleft
));
309 random_mix_timing_entropy();
313 //If we had to wait, check queues at least once more.
315 if(!modal_pause
&& !normal_pause
)
316 CORE().framerate
.unfreeze_time(framerate_regulator::get_utime());
319 void platform::set_paused(bool enable
) throw()
321 normal_pause
= enable
;
324 void platform::wait(uint64_t usec
) throw()
327 continue_time
= framerate_regulator::get_utime() + usec
;
328 bool run_idle
= false;
330 uint64_t now
= framerate_regulator::get_utime();
331 if(now
>= on_timer_time
) {
332 lua_callback_do_timer();
336 lua_callback_do_idle();
340 threads::alock
h(CORE().queue_lock
);
341 CORE().run_queue(true);
342 if(CORE().queue_function_run
)
344 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
347 now
= framerate_regulator::get_utime();
348 uint64_t waitleft
= 0;
349 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
350 waitleft
= min(static_cast<uint64_t>(MAXWAIT
), waitleft
);
352 if(now
>= on_idle_time
) {
356 if(on_idle_time
>= now
)
357 waitleft
= min(waitleft
, on_idle_time
- now
);
358 if(on_timer_time
>= now
)
359 waitleft
= min(waitleft
, on_timer_time
- now
);
361 threads::cv_timed_wait(CORE().queue_condition
, h
, threads::ustime(waitleft
));
362 random_mix_timing_entropy();
369 void platform::cancel_wait() throw()
372 threads::alock
h(CORE().queue_lock
);
373 CORE().queue_condition
.notify_all();
376 void platform::set_modal_pause(bool enable
) throw()
378 modal_pause
= enable
;
381 void platform::run_queues() throw()
383 CORE().run_queue(false);
388 threads::lock _msgbuf_lock
;
389 framebuffer::fb
<false>* our_screen
;
391 struct painter_listener
395 screenupdate
.set(notify_screen_update
, []() { graphics_driver_notify_screen(); });
396 statusupdate
.set(notify_status_update
, []() { graphics_driver_notify_status(); });
397 setscreen
.set(notify_set_screen
, [](framebuffer::fb
<false>& scr
) { our_screen
= &scr
; });
400 struct dispatch::target
<> screenupdate
;
401 struct dispatch::target
<> statusupdate
;
402 struct dispatch::target
<framebuffer::fb
<false>&> setscreen
;
406 threads::lock
& platform::msgbuf_lock() throw()
411 modal_pause_holder::modal_pause_holder()
413 platform::set_modal_pause(true);
416 modal_pause_holder::~modal_pause_holder()
418 platform::set_modal_pause(false);
421 bool platform::pausing_allowed
= true;
422 double platform::global_volume
= 1.0;
423 volatile bool queue_synchronous_fn_warning
;