1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framerate.hpp"
4 #include "core/misc.hpp"
5 #include "core/render.hpp"
6 #include "core/window.hpp"
11 #include <boost/iostreams/categories.hpp>
12 #include <boost/iostreams/copy.hpp>
13 #include <boost/iostreams/stream.hpp>
14 #include <boost/iostreams/stream_buffer.hpp>
15 #include <boost/iostreams/filter/symmetric.hpp>
16 #include <boost/iostreams/filter/zlib.hpp>
17 #include <boost/iostreams/filtering_stream.hpp>
18 #include <boost/iostreams/device/back_inserter.hpp>
20 #define MAXMESSAGES 5000
21 #define INIT_WIN_SIZE 6
23 mutex::holder::holder(mutex
& m
) throw()
29 mutex::holder::~holder() throw()
34 mutex::~mutex() throw()
38 mutex::mutex() throw()
42 condition::~condition() throw()
46 mutex
& condition::associated() throw()
51 condition::condition(mutex
& m
)
56 thread_id::thread_id() throw()
60 thread_id::~thread_id() throw()
64 thread::thread() throw()
70 thread::~thread() throw()
74 bool thread::is_alive() throw()
79 void* thread::join() throw()
87 void thread::notify_quit(void* ret
) throw()
100 keypress::keypress(modifier_set mod
, keygroup
& _key
, short _value
)
108 keypress::keypress(modifier_set mod
, keygroup
& _key
, keygroup
& _key2
, short _value
)
119 function_ptr_command
<> identify_key("show-plugins", "Show plugins in use",
120 "Syntax: show-plugins\nShows plugins in use.\n",
121 []() throw(std::bad_alloc
, std::runtime_error
) {
122 messages
<< "Graphics:\t" << graphics_plugin::name
<< std::endl
;
123 messages
<< "Sound:\t" << sound_plugin::name
<< std::endl
;
124 messages
<< "Joystick:\t" << joystick_plugin::name
<< std::endl
;
127 function_ptr_command
<const std::string
&> enable_sound("enable-sound", "Enable/Disable sound",
128 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
129 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
130 std::string s
= args
;
131 if(s
== "on" || s
== "true" || s
== "1" || s
== "enable" || s
== "enabled") {
132 if(!platform::sound_initialized())
133 throw std::runtime_error("Sound failed to initialize and is disabled");
134 platform::sound_enable(true);
135 } else if(s
== "off" || s
== "false" || s
== "0" || s
== "disable" || s
== "disabled") {
136 if(platform::sound_initialized())
137 platform::sound_enable(false);
139 throw std::runtime_error("Bad sound setting");
142 function_ptr_command
<const std::string
&> set_sound_device("set-sound-device", "Set sound device",
143 "Syntax: set-sound-device <id>\nSet sound device to <id>.\n",
144 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
145 if(!platform::sound_initialized())
146 throw std::runtime_error("Sound failed to initialize and is disabled");
147 platform::set_sound_device(args
);
150 function_ptr_command
<> get_sound_devices("show-sound-devices", "Show sound devices",
151 "Syntax: show-sound-devices\nShow listing of available sound devices\n",
152 []() throw(std::bad_alloc
, std::runtime_error
) {
153 if(!platform::sound_initialized())
154 throw std::runtime_error("Sound failed to initialize and is disabled");
155 auto r
= platform::get_sound_devices();
156 auto s
= platform::get_sound_device();
157 std::string dname
= "unknown";
160 messages
<< "Detected " << r
.size() << " sound output devices." << std::endl
;
162 messages
<< "Audio device " << i
.first
<< ": " << i
.second
<< std::endl
;
163 messages
<< "Currently using device " << platform::get_sound_device() << " ("
164 << dname
<< ")" << std::endl
;
167 function_ptr_command
<> get_sound_status("show-sound-status", "Show sound status",
168 "Syntax: show-sound-status\nShow current sound status\n",
169 []() throw(std::bad_alloc
, std::runtime_error
) {
170 messages
<< "Sound plugin: " << sound_plugin::name
<< std::endl
;
171 if(!platform::sound_initialized())
172 messages
<< "Sound initialization failed, sound disabled" << std::endl
;
174 auto r
= platform::get_sound_devices();
175 auto s
= platform::get_sound_device();
176 std::string dname
= "unknown";
179 messages
<< "Current sound device " << s
<< " (" << dname
<< ")" << std::endl
;
183 emulator_status emustatus
;
188 typedef char char_type
;
189 typedef boost::iostreams::sink_tag category
;
190 window_output(int* dummy
)
198 std::streamsize
write(const char* s
, std::streamsize n
)
200 size_t oldsize
= stream
.size();
201 stream
.resize(oldsize
+ n
);
202 memcpy(&stream
[oldsize
], s
, n
);
204 size_t lf
= stream
.size();
205 for(size_t i
= 0; i
< stream
.size(); i
++)
206 if(stream
[i
] == '\n') {
210 if(lf
== stream
.size())
212 std::string
foo(stream
.begin(), stream
.begin() + lf
);
213 platform::message(foo
);
214 if(lf
+ 1 < stream
.size())
215 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
216 stream
.resize(stream
.size() - lf
- 1);
221 std::vector
<char> stream
;
224 class msgcallback
: public messagebuffer::update_handler
227 ~msgcallback() throw() {};
228 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
230 platform::notify_message();
234 std::ofstream system_log
;
235 bool sounds_enabled
= true;
238 emulator_status
& platform::get_emustatus() throw()
243 void platform::sound_enable(bool enable
) throw()
245 sound_plugin::enable(enable
);
246 sounds_enabled
= enable
;
247 information_dispatch::do_sound_unmute(enable
);
250 void platform::set_sound_device(const std::string
& dev
) throw()
253 sound_plugin::set_device(dev
);
254 } catch(std::exception
& e
) {
255 out() << "Error changing sound device: " << e
.what() << std::endl
;
257 //After failed change, we don't know what is selected.
258 information_dispatch::do_sound_change(sound_plugin::get_device());
261 bool platform::is_sound_enabled() throw()
263 return sounds_enabled
;
267 void platform::init()
269 msgbuf
.register_handler(msg_callback_obj
);
270 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
271 time_t curtime
= time(NULL
);
272 struct tm
* tm
= localtime(&curtime
);
274 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
275 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
276 system_log
<< "lsnes started at " << buffer
<< std::endl
;
277 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
279 graphics_plugin::init();
280 sound_plugin::init();
281 joystick_plugin::init();
284 void platform::quit()
286 joystick_plugin::quit();
287 sound_plugin::quit();
288 graphics_plugin::quit();
289 msgbuf
.unregister_handler(msg_callback_obj
);
290 time_t curtime
= time(NULL
);
291 struct tm
* tm
= localtime(&curtime
);
293 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
294 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
295 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
296 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
300 std::ostream
& platform::out() throw(std::bad_alloc
)
302 static std::ostream
* cached
= NULL
;
305 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
309 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
312 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
314 mutex::holder
h(msgbuf_lock());
315 std::string msg2
= msg
;
317 size_t s
= msg2
.find_first_of("\n");
319 if(s
>= msg2
.length()) {
320 msgbuf
.add_message(forlog
= msg2
);
322 system_log
<< forlog
<< std::endl
;
325 msgbuf
.add_message(forlog
= msg2
.substr(0, s
));
327 system_log
<< forlog
<< std::endl
;
328 msg2
= msg2
.substr(s
+ 1);
333 void platform::fatal_error() throw()
335 time_t curtime
= time(NULL
);
336 struct tm
* tm
= localtime(&curtime
);
338 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
339 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
340 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
341 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
343 graphics_plugin::fatal_error();
350 condition
* queue_condition
;
351 std::deque
<keypress
> keypresses
;
352 std::deque
<std::string
> commands
;
353 std::deque
<std::pair
<void(*)(void*), void*>> functions
;
354 volatile bool normal_pause
;
355 volatile bool modal_pause
;
356 volatile uint64_t continue_time
;
357 volatile uint64_t next_function
;
358 volatile uint64_t functions_executed
;
360 void init_threading()
363 queue_lock
= &mutex::aquire();
365 queue_condition
= &condition::aquire(*queue_lock
);
368 void internal_run_queues(bool unlocked
) throw()
375 while(!keypresses
.empty()) {
376 keypress k
= keypresses
.front();
377 keypresses
.pop_front();
378 queue_lock
->unlock();
380 k
.key1
->set_position(k
.value
, k
.modifiers
);
382 k
.key2
->set_position(k
.value
, k
.modifiers
);
386 while(!commands
.empty()) {
387 std::string c
= commands
.front();
388 commands
.pop_front();
389 queue_lock
->unlock();
394 while(!functions
.empty()) {
395 std::pair
<void(*)(void*), void*> f
= functions
.front();
396 functions
.pop_front();
397 queue_lock
->unlock();
400 ++functions_executed
;
402 queue_condition
->signal();
403 } catch(std::bad_alloc
& e
) {
405 } catch(std::exception
& e
) {
406 std::cerr
<< "Fault inside platform::run_queues(): " << e
.what() << std::endl
;
410 queue_lock
->unlock();
414 #define MAXWAIT 10000
416 void platform::flush_command_queue() throw()
420 mutex::holder
h(*queue_lock
);
421 internal_run_queues(true);
422 uint64_t now
= get_utime();
423 uint64_t waitleft
= 0;
424 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
425 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
426 waitleft
= (waitleft
> MAXWAIT
) ? MAXWAIT
: waitleft
;
428 queue_condition
->wait(waitleft
);
431 //If we had to wait, check queues at least once more.
435 void platform::set_paused(bool enable
) throw()
437 normal_pause
= enable
;
440 void platform::wait(uint64_t usec
) throw()
442 continue_time
= get_utime() + usec
;
445 mutex::holder
h(*queue_lock
);
446 internal_run_queues(true);
447 uint64_t now
= get_utime();
448 uint64_t waitleft
= 0;
449 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
450 waitleft
= (waitleft
> MAXWAIT
) ? MAXWAIT
: waitleft
;
452 queue_condition
->wait(waitleft
);
458 void platform::cancel_wait() throw()
462 mutex::holder
h(*queue_lock
);
463 queue_condition
->signal();
466 void platform::set_modal_pause(bool enable
) throw()
468 modal_pause
= enable
;
471 void platform::queue(const keypress
& k
) throw(std::bad_alloc
)
474 mutex::holder
h(*queue_lock
);
475 keypresses
.push_back(k
);
476 queue_condition
->signal();
479 void platform::queue(const std::string
& c
) throw(std::bad_alloc
)
482 mutex::holder
h(*queue_lock
);
483 commands
.push_back(c
);
484 queue_condition
->signal();
487 void platform::queue(void (*f
)(void* arg
), void* arg
, bool sync
) throw(std::bad_alloc
)
490 mutex::holder
h(*queue_lock
);
492 functions
.push_back(std::make_pair(f
, arg
));
493 queue_condition
->signal();
495 while(functions_executed
< next_function
)
496 queue_condition
->wait(10000);
499 void platform::run_queues() throw()
501 internal_run_queues(false);
509 void trigger_repaint()
511 graphics_plugin::notify_screen();
514 struct painter_listener
: public information_dispatch
517 void on_set_screen(screen
& scr
);
518 void on_screen_update();
519 void on_status_update();
522 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
524 void painter_listener::on_set_screen(screen
& scr
)
529 void painter_listener::on_screen_update()
534 void painter_listener::on_status_update()
536 graphics_plugin::notify_status();
539 struct handle_mouse_request
546 void _handle_mouse(void* args
)
548 struct handle_mouse_request
* x
= reinterpret_cast<struct handle_mouse_request
*>(args
);
549 information_dispatch::do_click(x
->x
, x
->y
, x
->mask
);
553 void send_mouse_click(long x
, long y
, uint32_t buttons
)
555 struct handle_mouse_request z
;
559 platform::queue(_handle_mouse
, &z
, true);
562 mutex
& platform::msgbuf_lock() throw()
566 _msgbuf_lock
= &mutex::aquire();
570 return *_msgbuf_lock
;
573 void platform::screen_set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
577 if(our_screen
->palette_r
== rshift
&&
578 our_screen
->palette_g
== gshift
&&
579 our_screen
->palette_b
== bshift
)
581 our_screen
->set_palette(rshift
, gshift
, bshift
);