Hex editor
[lsnes.git] / src / core / window.cpp
blob8ee3fe345d31fd39ffe13a2b5a11e7ea822b99ad
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 namespace
35 volatile bool _system_thread_available = false;
38 keypress::keypress()
40 key1 = NULL;
41 key2 = NULL;
42 value = 0;
45 keypress::keypress(keyboard_modifier_set mod, keyboard_key& _key, short _value)
47 modifiers = mod;
48 key1 = &_key;
49 key2 = NULL;
50 value = _value;
53 keypress::keypress(keyboard_modifier_set mod, keyboard_key& _key, keyboard_key& _key2, short _value)
55 modifiers = mod;
56 key1 = &_key;
57 key2 = &_key2;
58 value = _value;
61 volatile bool platform::do_exit_dummy_event_loop = false;
63 namespace
65 bool queue_function_run = false;
67 function_ptr_command<> identify_key(lsnes_cmd, "show-plugins", "Show plugins in use",
68 "Syntax: show-plugins\nShows plugins in use.\n",
69 []() throw(std::bad_alloc, std::runtime_error) {
70 messages << "Graphics:\t" << graphics_driver_name() << std::endl;
71 messages << "Sound:\t" << audioapi_driver_name() << std::endl;
72 messages << "Joystick:\t" << joystick_driver_name() << std::endl;
73 });
75 function_ptr_command<const std::string&> enable_sound(lsnes_cmd, "enable-sound", "Enable/Disable sound",
76 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
77 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
78 switch(string_to_bool(args)) {
79 case 1:
80 if(!audioapi_driver_initialized())
81 throw std::runtime_error("Sound failed to initialize and is disabled");
82 platform::sound_enable(true);
83 break;
84 case 0:
85 if(audioapi_driver_initialized())
86 platform::sound_enable(false);
87 break;
88 default:
89 throw std::runtime_error("Bad sound setting");
91 });
93 inverse_bind ienable_sound(lsnes_mapper, "enable-sound on", "Sound‣Enable");
94 inverse_bind idisable_sound(lsnes_mapper, "enable-sound off", "Sound‣Disable");
96 emulator_status emustatus;
98 class window_output
100 public:
101 typedef char char_type;
102 typedef boost::iostreams::sink_tag category;
103 window_output(int* dummy)
107 void close()
111 std::streamsize write(const char* s, std::streamsize n)
113 size_t oldsize = stream.size();
114 stream.resize(oldsize + n);
115 memcpy(&stream[oldsize], s, n);
116 while(true) {
117 size_t lf = stream.size();
118 for(size_t i = 0; i < stream.size(); i++)
119 if(stream[i] == '\n') {
120 lf = i;
121 break;
123 if(lf == stream.size())
124 break;
125 std::string foo(stream.begin(), stream.begin() + lf);
126 platform::message(foo);
127 if(lf + 1 < stream.size())
128 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
129 stream.resize(stream.size() - lf - 1);
131 return n;
133 protected:
134 std::vector<char> stream;
137 class msgcallback : public messagebuffer::update_handler
139 public:
140 ~msgcallback() throw() {};
141 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
143 platform::notify_message();
145 } msg_callback_obj;
147 std::ofstream system_log;
148 bool sounds_enabled = true;
151 emulator_status& platform::get_emustatus() throw()
153 return emustatus;
156 void platform::sound_enable(bool enable) throw()
158 audioapi_driver_enable(enable);
159 sounds_enabled = enable;
160 notify_sound_unmute(enable);
163 void platform::set_sound_device(const std::string& pdev, const std::string& rdev) throw()
165 std::string old_play = audioapi_driver_get_device(false);
166 std::string old_rec = audioapi_driver_get_device(true);
167 try {
168 audioapi_driver_set_device(pdev, rdev);
169 } catch(std::exception& e) {
170 out() << "Error changing sound device: " << e.what() << std::endl;
171 //Try to restore the device.
172 try {
173 audioapi_driver_set_device(old_play, old_rec);
174 } catch(...) {
177 //After failed change, we don't know what is selected.
178 notify_sound_change(std::make_pair(audioapi_driver_get_device(true), audioapi_driver_get_device(false)));
181 bool platform::is_sound_enabled() throw()
183 return sounds_enabled;
187 void platform::init()
189 do_exit_dummy_event_loop = false;
190 msgbuf.register_handler(msg_callback_obj);
191 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
192 time_t curtime = time(NULL);
193 struct tm* tm = localtime(&curtime);
194 char buffer[1024];
195 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
196 system_log << "-----------------------------------------------------------------------" << std::endl;
197 system_log << "lsnes started at " << buffer << std::endl;
198 system_log << "-----------------------------------------------------------------------" << std::endl;
199 do_init_font();
200 graphics_driver_init();
201 audioapi_init();
202 audioapi_driver_init();
203 joystick_driver_init();
206 void platform::quit()
208 joystick_driver_quit();
209 audioapi_driver_quit();
210 audioapi_quit();
211 graphics_driver_quit();
212 msgbuf.unregister_handler(msg_callback_obj);
213 time_t curtime = time(NULL);
214 struct tm* tm = localtime(&curtime);
215 char buffer[1024];
216 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
217 system_log << "-----------------------------------------------------------------------" << std::endl;
218 system_log << "lsnes shutting down at " << buffer << std::endl;
219 system_log << "-----------------------------------------------------------------------" << std::endl;
220 system_log.close();
223 std::ostream& platform::out() throw(std::bad_alloc)
225 static std::ostream* cached = NULL;
226 int dummy;
227 if(!cached)
228 cached = new boost::iostreams::stream<window_output>(&dummy);
229 return *cached;
232 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
235 void platform::message(const std::string& msg) throw(std::bad_alloc)
237 umutex_class h(msgbuf_lock());
238 std::string msg2 = msg;
239 while(msg2 != "") {
240 std::string forlog;
241 extract_token(msg2, forlog, "\n");
242 msgbuf.add_message(forlog);
243 if(system_log)
244 system_log << forlog << std::endl;
248 void platform::fatal_error() throw()
250 time_t curtime = time(NULL);
251 struct tm* tm = localtime(&curtime);
252 char buffer[1024];
253 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
254 system_log << "-----------------------------------------------------------------------" << std::endl;
255 system_log << "lsnes paniced at " << buffer << std::endl;
256 system_log << "-----------------------------------------------------------------------" << std::endl;
257 system_log.close();
258 graphics_driver_fatal_error();
259 exit(1);
262 namespace
264 mutex_class queue_lock;
265 cv_class queue_condition;
266 std::deque<keypress> keypresses;
267 std::deque<std::string> commands;
268 std::deque<std::pair<void(*)(void*), void*>> functions;
269 volatile bool normal_pause;
270 volatile bool modal_pause;
271 volatile uint64_t continue_time;
272 volatile uint64_t next_function;
273 volatile uint64_t functions_executed;
275 void init_threading()
279 void internal_run_queues(bool unlocked) throw()
281 init_threading();
282 if(!unlocked)
283 queue_lock.lock();
284 try {
285 //Flush keypresses.
286 while(!keypresses.empty()) {
287 keypress k = keypresses.front();
288 keypresses.pop_front();
289 queue_lock.unlock();
290 if(k.key1)
291 k.key1->set_state(k.modifiers, k.value);
292 if(k.key2)
293 k.key2->set_state(k.modifiers, k.value);
294 queue_lock.lock();
295 queue_function_run = true;
297 //Flush commands.
298 while(!commands.empty()) {
299 std::string c = commands.front();
300 commands.pop_front();
301 queue_lock.unlock();
302 lsnes_cmd.invoke(c);
303 queue_lock.lock();
304 queue_function_run = true;
306 //Flush functions.
307 while(!functions.empty()) {
308 std::pair<void(*)(void*), void*> f = functions.front();
309 functions.pop_front();
310 queue_lock.unlock();
311 f.first(f.second);
312 queue_lock.lock();
313 ++functions_executed;
314 queue_function_run = true;
316 queue_condition.notify_all();
317 } catch(std::bad_alloc& e) {
318 OOM_panic();
319 } catch(std::exception& e) {
320 std::cerr << "Fault inside platform::run_queues(): " << e.what() << std::endl;
321 exit(1);
323 if(!unlocked)
324 queue_lock.unlock();
327 uint64_t on_idle_time;
328 uint64_t on_timer_time;
329 void reload_lua_timers()
331 on_idle_time = lua_timed_hook(LUA_TIMED_HOOK_IDLE);
332 on_timer_time = lua_timed_hook(LUA_TIMED_HOOK_TIMER);
333 queue_function_run = false;
337 #define MAXWAIT 100000ULL
339 void platform::dummy_event_loop() throw()
341 init_threading();
342 while(!do_exit_dummy_event_loop) {
343 umutex_class h(queue_lock);
344 internal_run_queues(true);
345 cv_timed_wait(queue_condition, h, microsec_class(MAXWAIT));
349 void platform::exit_dummy_event_loop() throw()
351 init_threading();
352 do_exit_dummy_event_loop = true;
353 umutex_class h(queue_lock);
354 queue_condition.notify_all();
355 usleep(200000);
358 void platform::flush_command_queue() throw()
360 reload_lua_timers();
361 queue_function_run = false;
362 if(modal_pause || normal_pause)
363 freeze_time(get_utime());
364 init_threading();
365 bool run_idle = false;
366 while(true) {
367 uint64_t now = get_utime();
368 if(now >= on_timer_time) {
369 lua_callback_do_timer();
370 reload_lua_timers();
372 if(run_idle) {
373 lua_callback_do_idle();
374 reload_lua_timers();
375 run_idle = false;
377 umutex_class h(queue_lock);
378 internal_run_queues(true);
379 if(!pausing_allowed)
380 break;
381 if(queue_function_run)
382 reload_lua_timers();
383 now = get_utime();
384 uint64_t waitleft = 0;
385 waitleft = (now < continue_time) ? (continue_time - now) : 0;
386 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
387 waitleft = min(waitleft, static_cast<uint64_t>(MAXWAIT));
388 if(waitleft > 0) {
389 if(now >= on_idle_time) {
390 run_idle = true;
391 waitleft = 0;
393 if(on_idle_time >= now)
394 waitleft = min(waitleft, on_idle_time - now);
395 if(on_timer_time >= now)
396 waitleft = min(waitleft, on_timer_time - now);
397 if(waitleft > 0)
398 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
399 } else
400 break;
401 //If we had to wait, check queues at least once more.
403 if(!modal_pause && !normal_pause)
404 unfreeze_time(get_utime());
407 void platform::set_paused(bool enable) throw()
409 normal_pause = enable;
412 void platform::wait(uint64_t usec) throw()
414 reload_lua_timers();
415 continue_time = get_utime() + usec;
416 init_threading();
417 bool run_idle = false;
418 while(true) {
419 uint64_t now = get_utime();
420 if(now >= on_timer_time) {
421 lua_callback_do_timer();
422 reload_lua_timers();
424 if(run_idle) {
425 lua_callback_do_idle();
426 run_idle = false;
427 reload_lua_timers();
429 umutex_class h(queue_lock);
430 internal_run_queues(true);
431 if(queue_function_run)
432 reload_lua_timers();
433 now = get_utime();
434 uint64_t waitleft = 0;
435 waitleft = (now < continue_time) ? (continue_time - now) : 0;
436 waitleft = min(static_cast<uint64_t>(MAXWAIT), waitleft);
437 if(waitleft > 0) {
438 if(now >= on_idle_time) {
439 run_idle = true;
440 waitleft = 0;
442 if(on_idle_time >= now)
443 waitleft = min(waitleft, on_idle_time - now);
444 if(on_timer_time >= now)
445 waitleft = min(waitleft, on_timer_time - now);
446 if(waitleft > 0)
447 cv_timed_wait(queue_condition, h, microsec_class(waitleft));
448 } else
449 return;
453 void platform::cancel_wait() throw()
455 init_threading();
456 continue_time = 0;
457 umutex_class h(queue_lock);
458 queue_condition.notify_all();
461 void platform::set_modal_pause(bool enable) throw()
463 modal_pause = enable;
466 void platform::queue(const keypress& k) throw(std::bad_alloc)
468 init_threading();
469 umutex_class h(queue_lock);
470 keypresses.push_back(k);
471 queue_condition.notify_all();
474 void platform::queue(const std::string& c) throw(std::bad_alloc)
476 init_threading();
477 umutex_class h(queue_lock);
478 commands.push_back(c);
479 queue_condition.notify_all();
482 void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
484 if(!_system_thread_available) {
485 f(arg);
486 return;
488 init_threading();
489 umutex_class h(queue_lock);
490 ++next_function;
491 functions.push_back(std::make_pair(f, arg));
492 queue_condition.notify_all();
493 if(sync)
494 while(functions_executed < next_function && _system_thread_available)
495 cv_timed_wait(queue_condition, h, microsec_class(10000));
498 void platform::run_queues() throw()
500 internal_run_queues(false);
503 void platform::system_thread_available(bool av) throw()
505 _system_thread_available = av;
508 namespace
510 mutex_class _msgbuf_lock;
511 framebuffer<false>* our_screen;
513 struct painter_listener
515 painter_listener()
517 screenupdate.set(notify_screen_update, []() { graphics_driver_notify_screen(); });
518 statusupdate.set(notify_status_update, []() { graphics_driver_notify_status(); });
519 setscreen.set(notify_set_screen, [](framebuffer<false>& scr) { our_screen = &scr; });
521 private:
522 struct dispatch_target<> screenupdate;
523 struct dispatch_target<> statusupdate;
524 struct dispatch_target<framebuffer<false>&> setscreen;
525 } x;
528 mutex_class& platform::msgbuf_lock() throw()
530 return _msgbuf_lock;
533 void platform::screen_set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
535 if(!our_screen)
536 return;
537 if(our_screen->get_palette_r() == rshift &&
538 our_screen->get_palette_g() == gshift &&
539 our_screen->get_palette_b() == bshift)
540 return;
541 our_screen->set_palette(rshift, gshift, bshift);
542 graphics_driver_notify_screen();
545 modal_pause_holder::modal_pause_holder()
547 platform::set_modal_pause(true);
550 modal_pause_holder::~modal_pause_holder()
552 platform::set_modal_pause(false);
555 bool platform::pausing_allowed = true;
556 double platform::global_volume = 1.0;
557 volatile bool queue_synchronous_fn_warning;