Instancefy lua core stuff
[lsnes.git] / src / core / window.cpp
blob8b8d153a5fe76e9d2b9756a93df3efbd775d67f0
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"
8 #include "core/messages.hpp"
9 #include "core/queue.hpp"
10 #include "core/random.hpp"
11 #include "core/window.hpp"
12 #include "fonts/wrapper.hpp"
13 #include "library/framebuffer.hpp"
14 #include "library/minmax.hpp"
15 #include "library/string.hpp"
16 #include "library/threads.hpp"
17 #include "lua/lua.hpp"
19 #include <fstream>
20 #include <iostream>
21 #include <string>
22 #include <deque>
23 #include <sys/time.h>
24 #include <unistd.h>
25 #include <boost/iostreams/categories.hpp>
26 #include <boost/iostreams/copy.hpp>
27 #include <boost/iostreams/stream.hpp>
28 #include <boost/iostreams/stream_buffer.hpp>
29 #include <boost/iostreams/filter/symmetric.hpp>
30 #include <boost/iostreams/filter/zlib.hpp>
31 #include <boost/iostreams/filtering_stream.hpp>
32 #include <boost/iostreams/device/back_inserter.hpp>
34 #define MAXMESSAGES 5000
35 #define INIT_WIN_SIZE 6
37 volatile bool platform::do_exit_dummy_event_loop = false;
39 namespace
41 command::fnptr<> identify_key(lsnes_cmds, "show-plugins", "Show plugins in use",
42 "Syntax: show-plugins\nShows plugins in use.\n",
43 []() throw(std::bad_alloc, std::runtime_error) {
44 messages << "Graphics:\t" << graphics_driver_name() << std::endl;
45 messages << "Sound:\t" << audioapi_driver_name() << std::endl;
46 messages << "Joystick:\t" << joystick_driver_name() << std::endl;
47 });
49 command::fnptr<const std::string&> enable_sound(lsnes_cmds, "enable-sound", "Enable/Disable sound",
50 "Syntax: enable-sound <on/off/toggle>\nEnable or disable sound.\n",
51 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
52 if(args == "toggle") {
53 if(!audioapi_driver_initialized())
54 throw std::runtime_error("Sound failed to initialize and is disabled");
55 platform::sound_enable(!platform::is_sound_enabled());
56 return;
58 switch(string_to_bool(args)) {
59 case 1:
60 if(!audioapi_driver_initialized())
61 throw std::runtime_error("Sound failed to initialize and is disabled");
62 platform::sound_enable(true);
63 break;
64 case 0:
65 if(audioapi_driver_initialized())
66 platform::sound_enable(false);
67 break;
68 default:
69 throw std::runtime_error("Bad sound setting");
71 });
73 keyboard::invbind_info ienable_sound(lsnes_invbinds, "enable-sound on", "Sound‣Enable");
74 keyboard::invbind_info idisable_sound(lsnes_invbinds, "enable-sound off", "Sound‣Disable");
75 keyboard::invbind_info itoggle_sound(lsnes_invbinds, "enable-sound toggle", "Sound‣Toggle");
77 class window_output
79 public:
80 typedef char char_type;
81 typedef boost::iostreams::sink_tag category;
82 window_output(int* dummy)
86 void close()
90 std::streamsize write(const char* s, std::streamsize n)
92 size_t oldsize = stream.size();
93 stream.resize(oldsize + n);
94 memcpy(&stream[oldsize], s, n);
95 while(true) {
96 size_t lf = stream.size();
97 for(size_t i = 0; i < stream.size(); i++)
98 if(stream[i] == '\n') {
99 lf = i;
100 break;
102 if(lf == stream.size())
103 break;
104 std::string foo(stream.begin(), stream.begin() + lf);
105 platform::message(foo);
106 if(lf + 1 < stream.size())
107 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
108 stream.resize(stream.size() - lf - 1);
110 return n;
112 protected:
113 std::vector<char> stream;
116 class msgcallback : public messagebuffer::update_handler
118 public:
119 ~msgcallback() throw() {};
120 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
122 platform::notify_message();
124 } msg_callback_obj;
126 std::ofstream system_log;
127 bool sounds_enabled = true;
130 void platform::sound_enable(bool enable) throw()
132 audioapi_driver_enable(enable);
133 sounds_enabled = enable;
134 CORE().dispatch->sound_unmute(enable);
137 void platform::set_sound_device(const std::string& pdev, const std::string& rdev) throw()
139 std::string old_play = audioapi_driver_get_device(false);
140 std::string old_rec = audioapi_driver_get_device(true);
141 try {
142 audioapi_driver_set_device(pdev, rdev);
143 } catch(std::exception& e) {
144 out() << "Error changing sound device: " << e.what() << std::endl;
145 //Try to restore the device.
146 try {
147 audioapi_driver_set_device(old_play, old_rec);
148 } catch(...) {
151 //After failed change, we don't know what is selected.
152 CORE().dispatch->sound_change(std::make_pair(audioapi_driver_get_device(true),
153 audioapi_driver_get_device(false)));
156 bool platform::is_sound_enabled() throw()
158 return sounds_enabled;
162 void platform::init()
164 do_exit_dummy_event_loop = false;
165 msgbuf.register_handler(msg_callback_obj);
166 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
167 time_t curtime = time(NULL);
168 struct tm* tm = localtime(&curtime);
169 char buffer[1024];
170 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
171 system_log << "-----------------------------------------------------------------------" << std::endl;
172 system_log << "lsnes started at " << buffer << std::endl;
173 system_log << "-----------------------------------------------------------------------" << std::endl;
174 do_init_font();
175 graphics_driver_init();
176 audioapi_init();
177 audioapi_driver_init();
178 joystick_driver_init();
181 void platform::quit()
183 joystick_driver_quit();
184 audioapi_driver_quit();
185 audioapi_quit();
186 graphics_driver_quit();
187 msgbuf.unregister_handler(msg_callback_obj);
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 shutting down at " << buffer << std::endl;
194 system_log << "-----------------------------------------------------------------------" << std::endl;
195 system_log.close();
198 std::ostream& platform::out() throw(std::bad_alloc)
200 static std::ostream* cached = NULL;
201 int dummy;
202 if(!cached)
203 cached = new boost::iostreams::stream<window_output>(&dummy);
204 return *cached;
207 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
210 void platform::message(const std::string& msg) throw(std::bad_alloc)
212 threads::alock h(msgbuf_lock());
213 for(auto& forlog : token_iterator_foreach(msg, {"\n"})) {
214 msgbuf.add_message(forlog);
215 if(system_log)
216 system_log << forlog << std::endl;
220 void platform::fatal_error() throw()
222 time_t curtime = time(NULL);
223 struct tm* tm = localtime(&curtime);
224 char buffer[1024];
225 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
226 system_log << "-----------------------------------------------------------------------" << std::endl;
227 system_log << "lsnes paniced at " << buffer << std::endl;
228 system_log << "-----------------------------------------------------------------------" << std::endl;
229 system_log.close();
230 graphics_driver_fatal_error();
231 exit(1);
234 namespace
236 volatile bool normal_pause;
237 volatile bool modal_pause;
238 volatile uint64_t continue_time;
241 uint64_t on_idle_time;
242 uint64_t on_timer_time;
243 void reload_lua_timers()
245 auto& core = CORE();
246 on_idle_time = core.lua2->timed_hook(LUA_TIMED_HOOK_IDLE);
247 on_timer_time = core.lua2->timed_hook(LUA_TIMED_HOOK_TIMER);
248 core.iqueue->queue_function_run = false;
252 #define MAXWAIT 100000ULL
254 void platform::dummy_event_loop() throw()
256 auto& core = CORE();
257 while(!do_exit_dummy_event_loop) {
258 threads::alock h(core.iqueue->queue_lock);
259 core.iqueue->run_queue(true);
260 threads::cv_timed_wait(core.iqueue->queue_condition, h, threads::ustime(MAXWAIT));
261 random_mix_timing_entropy();
265 void platform::exit_dummy_event_loop() throw()
267 auto& core = CORE();
268 do_exit_dummy_event_loop = true;
269 threads::alock h(core.iqueue->queue_lock);
270 core.iqueue->queue_condition.notify_all();
271 usleep(200000);
274 void platform::flush_command_queue() throw()
276 auto& core = CORE();
277 reload_lua_timers();
278 core.iqueue->queue_function_run = false;
279 if(modal_pause || normal_pause)
280 core.framerate->freeze_time(framerate_regulator::get_utime());
281 bool run_idle = false;
282 while(true) {
283 uint64_t now = framerate_regulator::get_utime();
284 if(now >= on_timer_time) {
285 core.lua2->callback_do_timer();
286 reload_lua_timers();
288 if(run_idle) {
289 core.lua2->callback_do_idle();
290 reload_lua_timers();
291 run_idle = false;
293 threads::alock h(core.iqueue->queue_lock);
294 core.iqueue->run_queue(true);
295 if(!pausing_allowed)
296 break;
297 if(core.iqueue->queue_function_run)
298 reload_lua_timers();
299 now = framerate_regulator::get_utime();
300 uint64_t waitleft = 0;
301 waitleft = (now < continue_time) ? (continue_time - now) : 0;
302 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
303 waitleft = min(waitleft, static_cast<uint64_t>(MAXWAIT));
304 if(waitleft > 0) {
305 if(now >= on_idle_time) {
306 run_idle = true;
307 waitleft = 0;
309 if(on_idle_time >= now)
310 waitleft = min(waitleft, on_idle_time - now);
311 if(on_timer_time >= now)
312 waitleft = min(waitleft, on_timer_time - now);
313 if(waitleft > 0) {
314 threads::cv_timed_wait(core.iqueue->queue_condition, h, threads::ustime(waitleft));
315 random_mix_timing_entropy();
317 } else
318 break;
319 //If we had to wait, check queues at least once more.
321 if(!modal_pause && !normal_pause)
322 core.framerate->unfreeze_time(framerate_regulator::get_utime());
325 void platform::set_paused(bool enable) throw()
327 normal_pause = enable;
330 void platform::wait(uint64_t usec) throw()
332 auto& core = CORE();
333 reload_lua_timers();
334 continue_time = framerate_regulator::get_utime() + usec;
335 bool run_idle = false;
336 while(true) {
337 uint64_t now = framerate_regulator::get_utime();
338 if(now >= on_timer_time) {
339 core.lua2->callback_do_timer();
340 reload_lua_timers();
342 if(run_idle) {
343 core.lua2->callback_do_idle();
344 run_idle = false;
345 reload_lua_timers();
347 threads::alock h(core.iqueue->queue_lock);
348 core.iqueue->run_queue(true);
349 if(core.iqueue->queue_function_run)
350 reload_lua_timers();
351 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
352 if(!usec)
353 return;
354 now = framerate_regulator::get_utime();
355 uint64_t waitleft = 0;
356 waitleft = (now < continue_time) ? (continue_time - now) : 0;
357 waitleft = min(static_cast<uint64_t>(MAXWAIT), waitleft);
358 if(waitleft > 0) {
359 if(now >= on_idle_time) {
360 run_idle = true;
361 waitleft = 0;
363 if(on_idle_time >= now)
364 waitleft = min(waitleft, on_idle_time - now);
365 if(on_timer_time >= now)
366 waitleft = min(waitleft, on_timer_time - now);
367 if(waitleft > 0) {
368 threads::cv_timed_wait(core.iqueue->queue_condition, h, threads::ustime(waitleft));
369 random_mix_timing_entropy();
371 } else
372 return;
376 void platform::cancel_wait() throw()
378 auto& core = CORE();
379 continue_time = 0;
380 threads::alock h(core.iqueue->queue_lock);
381 core.iqueue->queue_condition.notify_all();
384 void platform::set_modal_pause(bool enable) throw()
386 modal_pause = enable;
389 void platform::run_queues() throw()
391 CORE().iqueue->run_queue(false);
394 namespace
396 threads::lock _msgbuf_lock;
399 threads::lock& platform::msgbuf_lock() throw()
401 return _msgbuf_lock;
404 modal_pause_holder::modal_pause_holder()
406 platform::set_modal_pause(true);
409 modal_pause_holder::~modal_pause_holder()
411 platform::set_modal_pause(false);
414 bool platform::pausing_allowed = true;
415 double platform::global_volume = 1.0;
416 volatile bool queue_synchronous_fn_warning;