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"
12 #include <boost/iostreams/categories.hpp>
13 #include <boost/iostreams/copy.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #include <boost/iostreams/stream_buffer.hpp>
16 #include <boost/iostreams/filter/symmetric.hpp>
17 #include <boost/iostreams/filter/zlib.hpp>
18 #include <boost/iostreams/filtering_stream.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
21 #define MAXMESSAGES 5000
22 #define INIT_WIN_SIZE 6
24 mutex::holder::holder(mutex
& m
) throw()
30 mutex::holder::~holder() throw()
35 mutex::~mutex() throw()
39 mutex::mutex() throw()
43 condition::~condition() throw()
47 mutex
& condition::associated() throw()
52 condition::condition(mutex
& m
)
57 thread_id::thread_id() throw()
61 thread_id::~thread_id() throw()
65 thread::thread() throw()
71 thread::~thread() throw()
75 bool thread::is_alive() throw()
80 void* thread::join() throw()
88 void thread::notify_quit(void* ret
) throw()
101 keypress::keypress(modifier_set mod
, keygroup
& _key
, short _value
)
109 keypress::keypress(modifier_set mod
, keygroup
& _key
, keygroup
& _key2
, short _value
)
120 function_ptr_command
<> identify_key("show-plugins", "Show plugins in use",
121 "Syntax: show-plugins\nShows plugins in use.\n",
122 []() throw(std::bad_alloc
, std::runtime_error
) {
123 messages
<< "Graphics:\t" << graphics_plugin::name
<< std::endl
;
124 messages
<< "Sound:\t" << sound_plugin::name
<< std::endl
;
125 messages
<< "Joystick:\t" << joystick_plugin::name
<< std::endl
;
128 function_ptr_command
<const std::string
&> enable_sound("enable-sound", "Enable/Disable sound",
129 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
130 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
131 std::string s
= args
;
132 if(s
== "on" || s
== "true" || s
== "1" || s
== "enable" || s
== "enabled") {
133 if(!platform::sound_initialized())
134 throw std::runtime_error("Sound failed to initialize and is disabled");
135 platform::sound_enable(true);
136 } else if(s
== "off" || s
== "false" || s
== "0" || s
== "disable" || s
== "disabled") {
137 if(platform::sound_initialized())
138 platform::sound_enable(false);
140 throw std::runtime_error("Bad sound setting");
143 function_ptr_command
<const std::string
&> set_sound_device("set-sound-device", "Set sound device",
144 "Syntax: set-sound-device <id>\nSet sound device to <id>.\n",
145 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
146 if(!platform::sound_initialized())
147 throw std::runtime_error("Sound failed to initialize and is disabled");
148 platform::set_sound_device(args
);
151 function_ptr_command
<> get_sound_devices("show-sound-devices", "Show sound devices",
152 "Syntax: show-sound-devices\nShow listing of available sound devices\n",
153 []() throw(std::bad_alloc
, std::runtime_error
) {
154 if(!platform::sound_initialized())
155 throw std::runtime_error("Sound failed to initialize and is disabled");
156 auto r
= platform::get_sound_devices();
157 auto s
= platform::get_sound_device();
158 std::string dname
= "unknown";
161 messages
<< "Detected " << r
.size() << " sound output devices." << std::endl
;
163 messages
<< "Audio device " << i
.first
<< ": " << i
.second
<< std::endl
;
164 messages
<< "Currently using device " << platform::get_sound_device() << " ("
165 << dname
<< ")" << std::endl
;
168 function_ptr_command
<> get_sound_status("show-sound-status", "Show sound status",
169 "Syntax: show-sound-status\nShow current sound status\n",
170 []() throw(std::bad_alloc
, std::runtime_error
) {
171 messages
<< "Sound plugin: " << sound_plugin::name
<< std::endl
;
172 if(!platform::sound_initialized())
173 messages
<< "Sound initialization failed, sound disabled" << std::endl
;
175 auto r
= platform::get_sound_devices();
176 auto s
= platform::get_sound_device();
177 std::string dname
= "unknown";
180 messages
<< "Current sound device " << s
<< " (" << dname
<< ")" << std::endl
;
184 emulator_status emustatus
;
189 typedef char char_type
;
190 typedef boost::iostreams::sink_tag category
;
191 window_output(int* dummy
)
199 std::streamsize
write(const char* s
, std::streamsize n
)
201 size_t oldsize
= stream
.size();
202 stream
.resize(oldsize
+ n
);
203 memcpy(&stream
[oldsize
], s
, n
);
205 size_t lf
= stream
.size();
206 for(size_t i
= 0; i
< stream
.size(); i
++)
207 if(stream
[i
] == '\n') {
211 if(lf
== stream
.size())
213 std::string
foo(stream
.begin(), stream
.begin() + lf
);
214 platform::message(foo
);
215 if(lf
+ 1 < stream
.size())
216 memmove(&stream
[0], &stream
[lf
+ 1], stream
.size() - lf
- 1);
217 stream
.resize(stream
.size() - lf
- 1);
222 std::vector
<char> stream
;
225 class msgcallback
: public messagebuffer::update_handler
228 ~msgcallback() throw() {};
229 void messagebuffer_update() throw(std::bad_alloc
, std::runtime_error
)
231 platform::notify_message();
235 std::ofstream system_log
;
236 bool sounds_enabled
= true;
239 emulator_status
& platform::get_emustatus() throw()
244 void platform::sound_enable(bool enable
) throw()
246 sound_plugin::enable(enable
);
247 sounds_enabled
= enable
;
248 information_dispatch::do_sound_unmute(enable
);
251 void platform::set_sound_device(const std::string
& dev
) throw()
254 sound_plugin::set_device(dev
);
255 } catch(std::exception
& e
) {
256 out() << "Error changing sound device: " << e
.what() << std::endl
;
258 //After failed change, we don't know what is selected.
259 information_dispatch::do_sound_change(sound_plugin::get_device());
262 bool platform::is_sound_enabled() throw()
264 return sounds_enabled
;
268 void platform::init()
270 msgbuf
.register_handler(msg_callback_obj
);
271 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
272 time_t curtime
= time(NULL
);
273 struct tm
* tm
= localtime(&curtime
);
275 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
276 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
277 system_log
<< "lsnes started at " << buffer
<< std::endl
;
278 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
280 graphics_plugin::init();
281 sound_plugin::init();
282 joystick_plugin::init();
285 void platform::quit()
287 joystick_plugin::quit();
288 sound_plugin::quit();
289 graphics_plugin::quit();
290 msgbuf
.unregister_handler(msg_callback_obj
);
291 time_t curtime
= time(NULL
);
292 struct tm
* tm
= localtime(&curtime
);
294 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
295 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
296 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
297 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
301 std::ostream
& platform::out() throw(std::bad_alloc
)
303 static std::ostream
* cached
= NULL
;
306 cached
= new boost::iostreams::stream
<window_output
>(&dummy
);
310 messagebuffer
platform::msgbuf(MAXMESSAGES
, INIT_WIN_SIZE
);
313 void platform::message(const std::string
& msg
) throw(std::bad_alloc
)
315 mutex::holder
h(msgbuf_lock());
316 std::string msg2
= msg
;
318 size_t s
= msg2
.find_first_of("\n");
320 if(s
>= msg2
.length()) {
321 msgbuf
.add_message(forlog
= msg2
);
323 system_log
<< forlog
<< std::endl
;
326 msgbuf
.add_message(forlog
= msg2
.substr(0, s
));
328 system_log
<< forlog
<< std::endl
;
329 msg2
= msg2
.substr(s
+ 1);
334 void platform::fatal_error() throw()
336 time_t curtime
= time(NULL
);
337 struct tm
* tm
= localtime(&curtime
);
339 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
340 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
341 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
342 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
344 graphics_plugin::fatal_error();
351 condition
* queue_condition
;
352 std::deque
<keypress
> keypresses
;
353 std::deque
<std::string
> commands
;
354 std::deque
<std::pair
<void(*)(void*), void*>> functions
;
355 volatile bool normal_pause
;
356 volatile bool modal_pause
;
357 volatile uint64_t continue_time
;
358 volatile uint64_t next_function
;
359 volatile uint64_t functions_executed
;
361 void init_threading()
364 queue_lock
= &mutex::aquire();
366 queue_condition
= &condition::aquire(*queue_lock
);
369 void internal_run_queues(bool unlocked
) throw()
376 while(!keypresses
.empty()) {
377 keypress k
= keypresses
.front();
378 keypresses
.pop_front();
379 queue_lock
->unlock();
381 k
.key1
->set_position(k
.value
, k
.modifiers
);
383 k
.key2
->set_position(k
.value
, k
.modifiers
);
387 while(!commands
.empty()) {
388 std::string c
= commands
.front();
389 commands
.pop_front();
390 queue_lock
->unlock();
395 while(!functions
.empty()) {
396 std::pair
<void(*)(void*), void*> f
= functions
.front();
397 functions
.pop_front();
398 queue_lock
->unlock();
401 ++functions_executed
;
403 queue_condition
->signal();
404 } catch(std::bad_alloc
& e
) {
406 } catch(std::exception
& e
) {
407 std::cerr
<< "Fault inside platform::run_queues(): " << e
.what() << std::endl
;
411 queue_lock
->unlock();
415 #define MAXWAIT 10000
417 void platform::flush_command_queue() throw()
421 mutex::holder
h(*queue_lock
);
422 internal_run_queues(true);
423 uint64_t now
= get_utime();
424 uint64_t waitleft
= 0;
425 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
426 waitleft
= (modal_pause
|| normal_pause
) ? MAXWAIT
: waitleft
;
427 waitleft
= (waitleft
> MAXWAIT
) ? MAXWAIT
: waitleft
;
429 queue_condition
->wait(waitleft
);
432 //If we had to wait, check queues at least once more.
436 void platform::set_paused(bool enable
) throw()
438 normal_pause
= enable
;
441 void platform::wait(uint64_t usec
) throw()
443 continue_time
= get_utime() + usec
;
446 mutex::holder
h(*queue_lock
);
447 internal_run_queues(true);
448 uint64_t now
= get_utime();
449 uint64_t waitleft
= 0;
450 waitleft
= (now
< continue_time
) ? (continue_time
- now
) : 0;
451 waitleft
= (waitleft
> MAXWAIT
) ? MAXWAIT
: waitleft
;
453 queue_condition
->wait(waitleft
);
459 void platform::cancel_wait() throw()
463 mutex::holder
h(*queue_lock
);
464 queue_condition
->signal();
467 void platform::set_modal_pause(bool enable
) throw()
469 modal_pause
= enable
;
472 void platform::queue(const keypress
& k
) throw(std::bad_alloc
)
475 mutex::holder
h(*queue_lock
);
476 keypresses
.push_back(k
);
477 queue_condition
->signal();
480 void platform::queue(const std::string
& c
) throw(std::bad_alloc
)
483 mutex::holder
h(*queue_lock
);
484 commands
.push_back(c
);
485 queue_condition
->signal();
488 void platform::queue(void (*f
)(void* arg
), void* arg
, bool sync
) throw(std::bad_alloc
)
490 if(sync
&& queue_synchronous_fn_warning
)
491 std::cerr
<< "WARNING: Synchronous queue in callback to UI, this may deadlock!" << std::endl
;
493 mutex::holder
h(*queue_lock
);
495 functions
.push_back(std::make_pair(f
, arg
));
496 queue_condition
->signal();
498 while(functions_executed
< next_function
)
499 queue_condition
->wait(10000);
502 void platform::run_queues() throw()
504 internal_run_queues(false);
512 void trigger_repaint()
514 graphics_plugin::notify_screen();
517 struct painter_listener
: public information_dispatch
520 void on_set_screen(screen
& scr
);
521 void on_screen_update();
522 void on_status_update();
525 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
527 void painter_listener::on_set_screen(screen
& scr
)
532 void painter_listener::on_screen_update()
537 void painter_listener::on_status_update()
539 graphics_plugin::notify_status();
542 struct handle_mouse_request
549 void _handle_mouse(void* args
)
551 struct handle_mouse_request
* x
= reinterpret_cast<struct handle_mouse_request
*>(args
);
552 information_dispatch::do_click(x
->x
, x
->y
, x
->mask
);
556 void send_mouse_click(long x
, long y
, uint32_t buttons
)
558 struct handle_mouse_request z
;
562 platform::queue(_handle_mouse
, &z
, true);
565 mutex
& platform::msgbuf_lock() throw()
569 _msgbuf_lock
= &mutex::aquire();
573 return *_msgbuf_lock
;
576 void platform::screen_set_palette(unsigned rshift
, unsigned gshift
, unsigned bshift
) throw()
580 if(our_screen
->palette_r
== rshift
&&
581 our_screen
->palette_g
== gshift
&&
582 our_screen
->palette_b
== bshift
)
584 our_screen
->set_palette(rshift
, gshift
, bshift
);
588 volatile bool queue_synchronous_fn_warning
;