More cleanup via initializer lists
[lsnes.git] / src / core / window.cpp
bloba7fa056354a3a9e844b02dddcf022014372c232f
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"
6 #include "lua/lua.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"
15 #include <fstream>
16 #include <iostream>
17 #include <string>
18 #include <deque>
19 #include <sys/time.h>
20 #include <unistd.h>
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
33 keypress::keypress()
35 key1 = NULL;
36 key2 = NULL;
37 value = 0;
40 keypress::keypress(keyboard_modifier_set mod, keyboard_key& _key, short _value)
42 modifiers = mod;
43 key1 = &_key;
44 key2 = NULL;
45 value = _value;
48 keypress::keypress(keyboard_modifier_set mod, keyboard_key& _key, keyboard_key& _key2, short _value)
50 modifiers = mod;
51 key1 = &_key;
52 key2 = &_key2;
53 value = _value;
56 volatile bool platform::do_exit_dummy_event_loop = false;
58 namespace
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;
68 });
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)) {
74 case 1:
75 if(!audioapi_driver_initialized())
76 throw std::runtime_error("Sound failed to initialize and is disabled");
77 platform::sound_enable(true);
78 break;
79 case 0:
80 if(audioapi_driver_initialized())
81 platform::sound_enable(false);
82 break;
83 default:
84 throw std::runtime_error("Bad sound setting");
86 });
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;
93 class window_output
95 public:
96 typedef char char_type;
97 typedef boost::iostreams::sink_tag category;
98 window_output(int* dummy)
102 void close()
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);
111 while(true) {
112 size_t lf = stream.size();
113 for(size_t i = 0; i < stream.size(); i++)
114 if(stream[i] == '\n') {
115 lf = i;
116 break;
118 if(lf == stream.size())
119 break;
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);
126 return n;
128 protected:
129 std::vector<char> stream;
132 class msgcallback : public messagebuffer::update_handler
134 public:
135 ~msgcallback() throw() {};
136 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
138 platform::notify_message();
140 } msg_callback_obj;
142 std::ofstream system_log;
143 bool sounds_enabled = true;
146 emulator_status& platform::get_emustatus() throw()
148 return emustatus;
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);
162 try {
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.
167 try {
168 audioapi_driver_set_device(old_play, old_rec);
169 } catch(...) {
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);
190 char buffer[1024];
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;
195 do_init_font();
196 graphics_driver_init();
197 audioapi_init();
198 audioapi_driver_init();
199 joystick_driver_init();
202 void platform::quit()
204 joystick_driver_quit();
205 audioapi_driver_quit();
206 audioapi_quit();
207 graphics_driver_quit();
208 msgbuf.unregister_handler(msg_callback_obj);
209 time_t curtime = time(NULL);
210 struct tm* tm = localtime(&curtime);
211 char buffer[1024];
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;
216 system_log.close();
219 std::ostream& platform::out() throw(std::bad_alloc)
221 static std::ostream* cached = NULL;
222 int dummy;
223 if(!cached)
224 cached = new boost::iostreams::stream<window_output>(&dummy);
225 return *cached;
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;
235 while(msg2 != "") {
236 std::string forlog;
237 extract_token(msg2, forlog, "\n");
238 msgbuf.add_message(forlog);
239 if(system_log)
240 system_log << forlog << std::endl;
244 void platform::fatal_error() throw()
246 time_t curtime = time(NULL);
247 struct tm* tm = localtime(&curtime);
248 char buffer[1024];
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;
253 system_log.close();
254 graphics_driver_fatal_error();
255 exit(1);
258 namespace
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()
277 init_threading();
278 if(!unlocked)
279 queue_lock.lock();
280 try {
281 //Flush keypresses.
282 while(!keypresses.empty()) {
283 keypress k = keypresses.front();
284 keypresses.pop_front();
285 queue_lock.unlock();
286 if(k.key1)
287 k.key1->set_state(k.modifiers, k.value);
288 if(k.key2)
289 k.key2->set_state(k.modifiers, k.value);
290 queue_lock.lock();
291 queue_function_run = true;
293 //Flush commands.
294 while(!commands.empty()) {
295 std::string c = commands.front();
296 commands.pop_front();
297 queue_lock.unlock();
298 lsnes_cmd.invoke(c);
299 queue_lock.lock();
300 queue_function_run = true;
302 //Flush functions.
303 while(!functions.empty()) {
304 std::pair<void(*)(void*), void*> f = functions.front();
305 functions.pop_front();
306 queue_lock.unlock();
307 f.first(f.second);
308 queue_lock.lock();
309 ++functions_executed;
310 queue_function_run = true;
312 queue_condition.notify_all();
313 } catch(std::bad_alloc& e) {
314 OOM_panic();
315 } catch(std::exception& e) {
316 std::cerr << "Fault inside platform::run_queues(): " << e.what() << std::endl;
317 exit(1);
319 if(!unlocked)
320 queue_lock.unlock();
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()
337 init_threading();
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()
347 init_threading();
348 do_exit_dummy_event_loop = true;
349 umutex_class h(queue_lock);
350 queue_condition.notify_all();
351 usleep(200000);
354 void platform::flush_command_queue() throw()
356 reload_lua_timers();
357 queue_function_run = false;
358 if(modal_pause || normal_pause)
359 freeze_time(get_utime());
360 init_threading();
361 bool run_idle = false;
362 while(true) {
363 uint64_t now = get_utime();
364 if(now >= on_timer_time) {
365 lua_callback_do_timer();
366 reload_lua_timers();
368 if(run_idle) {
369 lua_callback_do_idle();
370 reload_lua_timers();
371 run_idle = false;
373 umutex_class h(queue_lock);
374 internal_run_queues(true);
375 if(!pausing_allowed)
376 break;
377 if(queue_function_run)
378 reload_lua_timers();
379 now = get_utime();
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));
384 if(waitleft > 0) {
385 if(now >= on_idle_time) {
386 run_idle = true;
387 waitleft = 0;
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);
393 if(waitleft > 0)
394 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
395 } else
396 break;
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()
410 reload_lua_timers();
411 continue_time = get_utime() + usec;
412 init_threading();
413 bool run_idle = false;
414 while(true) {
415 uint64_t now = get_utime();
416 if(now >= on_timer_time) {
417 lua_callback_do_timer();
418 reload_lua_timers();
420 if(run_idle) {
421 lua_callback_do_idle();
422 run_idle = false;
423 reload_lua_timers();
425 umutex_class h(queue_lock);
426 internal_run_queues(true);
427 if(queue_function_run)
428 reload_lua_timers();
429 now = get_utime();
430 uint64_t waitleft = 0;
431 waitleft = (now < continue_time) ? (continue_time - now) : 0;
432 waitleft = min(static_cast<uint64_t>(MAXWAIT), waitleft);
433 if(waitleft > 0) {
434 if(now >= on_idle_time) {
435 run_idle = true;
436 waitleft = 0;
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);
442 if(waitleft > 0)
443 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
444 } else
445 return;
449 void platform::cancel_wait() throw()
451 init_threading();
452 continue_time = 0;
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)
464 init_threading();
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)
472 init_threading();
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)
480 init_threading();
481 umutex_class h(queue_lock);
482 ++next_function;
483 functions.push_back(std::make_pair(f, arg));
484 queue_condition.notify_all();
485 if(sync)
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);
495 namespace
497 mutex_class _msgbuf_lock;
498 framebuffer<false>* our_screen;
500 struct painter_listener : public information_dispatch
502 painter_listener();
503 void on_set_screen(framebuffer<false>& scr);
504 void on_screen_update();
505 void on_status_update();
506 } x;
508 painter_listener::painter_listener() : information_dispatch("painter-listener") {}
510 void painter_listener::on_set_screen(framebuffer<false>& scr)
512 our_screen = &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()
528 return _msgbuf_lock;
531 void platform::screen_set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
533 if(!our_screen)
534 return;
535 if(our_screen->get_palette_r() == rshift &&
536 our_screen->get_palette_g() == gshift &&
537 our_screen->get_palette_b() == bshift)
538 return;
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;