Make various instance stuff to take references to other instance objs
[lsnes.git] / src / core / window.cpp
blob8eea7cb26f49d616c8cb647702bbd57dd8434a22
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 "lua/lua.hpp"
9 #include "core/misc.hpp"
10 #include "core/random.hpp"
11 #include "core/window.hpp"
12 #include "fonts/wrapper.hpp"
13 #include "library/framebuffer.hpp"
14 #include "library/string.hpp"
15 #include "library/minmax.hpp"
16 #include "library/threads.hpp"
18 #include <fstream>
19 #include <iostream>
20 #include <string>
21 #include <deque>
22 #include <sys/time.h>
23 #include <unistd.h>
24 #include <boost/iostreams/categories.hpp>
25 #include <boost/iostreams/copy.hpp>
26 #include <boost/iostreams/stream.hpp>
27 #include <boost/iostreams/stream_buffer.hpp>
28 #include <boost/iostreams/filter/symmetric.hpp>
29 #include <boost/iostreams/filter/zlib.hpp>
30 #include <boost/iostreams/filtering_stream.hpp>
31 #include <boost/iostreams/device/back_inserter.hpp>
33 #define MAXMESSAGES 5000
34 #define INIT_WIN_SIZE 6
36 volatile bool platform::do_exit_dummy_event_loop = false;
38 namespace
40 command::fnptr<> identify_key(lsnes_cmds, "show-plugins", "Show plugins in use",
41 "Syntax: show-plugins\nShows plugins in use.\n",
42 []() throw(std::bad_alloc, std::runtime_error) {
43 messages << "Graphics:\t" << graphics_driver_name() << std::endl;
44 messages << "Sound:\t" << audioapi_driver_name() << std::endl;
45 messages << "Joystick:\t" << joystick_driver_name() << std::endl;
46 });
48 command::fnptr<const std::string&> enable_sound(lsnes_cmds, "enable-sound", "Enable/Disable sound",
49 "Syntax: enable-sound <on/off/toggle>\nEnable or disable sound.\n",
50 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
51 if(args == "toggle") {
52 if(!audioapi_driver_initialized())
53 throw std::runtime_error("Sound failed to initialize and is disabled");
54 platform::sound_enable(!platform::is_sound_enabled());
55 return;
57 switch(string_to_bool(args)) {
58 case 1:
59 if(!audioapi_driver_initialized())
60 throw std::runtime_error("Sound failed to initialize and is disabled");
61 platform::sound_enable(true);
62 break;
63 case 0:
64 if(audioapi_driver_initialized())
65 platform::sound_enable(false);
66 break;
67 default:
68 throw std::runtime_error("Bad sound setting");
70 });
72 keyboard::invbind_info ienable_sound(lsnes_invbinds, "enable-sound on", "Sound‣Enable");
73 keyboard::invbind_info idisable_sound(lsnes_invbinds, "enable-sound off", "Sound‣Disable");
74 keyboard::invbind_info itoggle_sound(lsnes_invbinds, "enable-sound toggle", "Sound‣Toggle");
76 class window_output
78 public:
79 typedef char char_type;
80 typedef boost::iostreams::sink_tag category;
81 window_output(int* dummy)
85 void close()
89 std::streamsize write(const char* s, std::streamsize n)
91 size_t oldsize = stream.size();
92 stream.resize(oldsize + n);
93 memcpy(&stream[oldsize], s, n);
94 while(true) {
95 size_t lf = stream.size();
96 for(size_t i = 0; i < stream.size(); i++)
97 if(stream[i] == '\n') {
98 lf = i;
99 break;
101 if(lf == stream.size())
102 break;
103 std::string foo(stream.begin(), stream.begin() + lf);
104 platform::message(foo);
105 if(lf + 1 < stream.size())
106 memmove(&stream[0], &stream[lf + 1], stream.size() - lf - 1);
107 stream.resize(stream.size() - lf - 1);
109 return n;
111 protected:
112 std::vector<char> stream;
115 class msgcallback : public messagebuffer::update_handler
117 public:
118 ~msgcallback() throw() {};
119 void messagebuffer_update() throw(std::bad_alloc, std::runtime_error)
121 platform::notify_message();
123 } msg_callback_obj;
125 std::ofstream system_log;
126 bool sounds_enabled = true;
129 void platform::sound_enable(bool enable) throw()
131 audioapi_driver_enable(enable);
132 sounds_enabled = enable;
133 notify_sound_unmute(enable);
136 void platform::set_sound_device(const std::string& pdev, const std::string& rdev) throw()
138 std::string old_play = audioapi_driver_get_device(false);
139 std::string old_rec = audioapi_driver_get_device(true);
140 try {
141 audioapi_driver_set_device(pdev, rdev);
142 } catch(std::exception& e) {
143 out() << "Error changing sound device: " << e.what() << std::endl;
144 //Try to restore the device.
145 try {
146 audioapi_driver_set_device(old_play, old_rec);
147 } catch(...) {
150 //After failed change, we don't know what is selected.
151 notify_sound_change(std::make_pair(audioapi_driver_get_device(true), audioapi_driver_get_device(false)));
154 bool platform::is_sound_enabled() throw()
156 return sounds_enabled;
160 void platform::init()
162 do_exit_dummy_event_loop = false;
163 msgbuf.register_handler(msg_callback_obj);
164 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
165 time_t curtime = time(NULL);
166 struct tm* tm = localtime(&curtime);
167 char buffer[1024];
168 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
169 system_log << "-----------------------------------------------------------------------" << std::endl;
170 system_log << "lsnes started at " << buffer << std::endl;
171 system_log << "-----------------------------------------------------------------------" << std::endl;
172 do_init_font();
173 graphics_driver_init();
174 audioapi_init();
175 audioapi_driver_init();
176 joystick_driver_init();
179 void platform::quit()
181 joystick_driver_quit();
182 audioapi_driver_quit();
183 audioapi_quit();
184 graphics_driver_quit();
185 msgbuf.unregister_handler(msg_callback_obj);
186 time_t curtime = time(NULL);
187 struct tm* tm = localtime(&curtime);
188 char buffer[1024];
189 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
190 system_log << "-----------------------------------------------------------------------" << std::endl;
191 system_log << "lsnes shutting down at " << buffer << std::endl;
192 system_log << "-----------------------------------------------------------------------" << std::endl;
193 system_log.close();
196 std::ostream& platform::out() throw(std::bad_alloc)
198 static std::ostream* cached = NULL;
199 int dummy;
200 if(!cached)
201 cached = new boost::iostreams::stream<window_output>(&dummy);
202 return *cached;
205 messagebuffer platform::msgbuf(MAXMESSAGES, INIT_WIN_SIZE);
208 void platform::message(const std::string& msg) throw(std::bad_alloc)
210 threads::alock h(msgbuf_lock());
211 for(auto& forlog : token_iterator_foreach(msg, {"\n"})) {
212 msgbuf.add_message(forlog);
213 if(system_log)
214 system_log << forlog << std::endl;
218 void platform::fatal_error() throw()
220 time_t curtime = time(NULL);
221 struct tm* tm = localtime(&curtime);
222 char buffer[1024];
223 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
224 system_log << "-----------------------------------------------------------------------" << std::endl;
225 system_log << "lsnes paniced at " << buffer << std::endl;
226 system_log << "-----------------------------------------------------------------------" << std::endl;
227 system_log.close();
228 graphics_driver_fatal_error();
229 exit(1);
232 namespace
234 volatile bool normal_pause;
235 volatile bool modal_pause;
236 volatile uint64_t continue_time;
239 uint64_t on_idle_time;
240 uint64_t on_timer_time;
241 void reload_lua_timers()
243 on_idle_time = lua_timed_hook(LUA_TIMED_HOOK_IDLE);
244 on_timer_time = lua_timed_hook(LUA_TIMED_HOOK_TIMER);
245 CORE().iqueue.queue_function_run = false;
249 #define MAXWAIT 100000ULL
251 void platform::dummy_event_loop() throw()
253 while(!do_exit_dummy_event_loop) {
254 threads::alock h(CORE().iqueue.queue_lock);
255 CORE().iqueue.run_queue(true);
256 threads::cv_timed_wait(CORE().iqueue.queue_condition, h, threads::ustime(MAXWAIT));
257 random_mix_timing_entropy();
261 void platform::exit_dummy_event_loop() throw()
263 do_exit_dummy_event_loop = true;
264 threads::alock h(CORE().iqueue.queue_lock);
265 CORE().iqueue.queue_condition.notify_all();
266 usleep(200000);
269 void platform::flush_command_queue() throw()
271 reload_lua_timers();
272 CORE().iqueue.queue_function_run = false;
273 if(modal_pause || normal_pause)
274 CORE().framerate.freeze_time(framerate_regulator::get_utime());
275 bool run_idle = false;
276 while(true) {
277 uint64_t now = framerate_regulator::get_utime();
278 if(now >= on_timer_time) {
279 lua_callback_do_timer();
280 reload_lua_timers();
282 if(run_idle) {
283 lua_callback_do_idle();
284 reload_lua_timers();
285 run_idle = false;
287 threads::alock h(CORE().iqueue.queue_lock);
288 CORE().iqueue.run_queue(true);
289 if(!pausing_allowed)
290 break;
291 if(CORE().iqueue.queue_function_run)
292 reload_lua_timers();
293 now = framerate_regulator::get_utime();
294 uint64_t waitleft = 0;
295 waitleft = (now < continue_time) ? (continue_time - now) : 0;
296 waitleft = (modal_pause || normal_pause) ? MAXWAIT : waitleft;
297 waitleft = min(waitleft, static_cast<uint64_t>(MAXWAIT));
298 if(waitleft > 0) {
299 if(now >= on_idle_time) {
300 run_idle = true;
301 waitleft = 0;
303 if(on_idle_time >= now)
304 waitleft = min(waitleft, on_idle_time - now);
305 if(on_timer_time >= now)
306 waitleft = min(waitleft, on_timer_time - now);
307 if(waitleft > 0) {
308 threads::cv_timed_wait(CORE().iqueue.queue_condition, h, threads::ustime(waitleft));
309 random_mix_timing_entropy();
311 } else
312 break;
313 //If we had to wait, check queues at least once more.
315 if(!modal_pause && !normal_pause)
316 CORE().framerate.unfreeze_time(framerate_regulator::get_utime());
319 void platform::set_paused(bool enable) throw()
321 normal_pause = enable;
324 void platform::wait(uint64_t usec) throw()
326 reload_lua_timers();
327 continue_time = framerate_regulator::get_utime() + usec;
328 bool run_idle = false;
329 while(true) {
330 uint64_t now = framerate_regulator::get_utime();
331 if(now >= on_timer_time) {
332 lua_callback_do_timer();
333 reload_lua_timers();
335 if(run_idle) {
336 lua_callback_do_idle();
337 run_idle = false;
338 reload_lua_timers();
340 threads::alock h(CORE().iqueue.queue_lock);
341 CORE().iqueue.run_queue(true);
342 if(CORE().iqueue.queue_function_run)
343 reload_lua_timers();
344 //If usec is 0, never wait (waitleft can be nonzero if time counting screws up).
345 if(!usec)
346 return;
347 now = framerate_regulator::get_utime();
348 uint64_t waitleft = 0;
349 waitleft = (now < continue_time) ? (continue_time - now) : 0;
350 waitleft = min(static_cast<uint64_t>(MAXWAIT), waitleft);
351 if(waitleft > 0) {
352 if(now >= on_idle_time) {
353 run_idle = true;
354 waitleft = 0;
356 if(on_idle_time >= now)
357 waitleft = min(waitleft, on_idle_time - now);
358 if(on_timer_time >= now)
359 waitleft = min(waitleft, on_timer_time - now);
360 if(waitleft > 0) {
361 threads::cv_timed_wait(CORE().iqueue.queue_condition, h, threads::ustime(waitleft));
362 random_mix_timing_entropy();
364 } else
365 return;
369 void platform::cancel_wait() throw()
371 continue_time = 0;
372 threads::alock h(CORE().iqueue.queue_lock);
373 CORE().iqueue.queue_condition.notify_all();
376 void platform::set_modal_pause(bool enable) throw()
378 modal_pause = enable;
381 void platform::run_queues() throw()
383 CORE().iqueue.run_queue(false);
386 namespace
388 threads::lock _msgbuf_lock;
389 framebuffer::fb<false>* our_screen;
391 struct painter_listener
393 painter_listener()
395 screenupdate.set(notify_screen_update, []() { graphics_driver_notify_screen(); });
396 statusupdate.set(notify_status_update, []() { graphics_driver_notify_status(); });
397 setscreen.set(notify_set_screen, [](framebuffer::fb<false>& scr) { our_screen = &scr; });
399 private:
400 struct dispatch::target<> screenupdate;
401 struct dispatch::target<> statusupdate;
402 struct dispatch::target<framebuffer::fb<false>&> setscreen;
403 } x;
406 threads::lock& platform::msgbuf_lock() throw()
408 return _msgbuf_lock;
411 modal_pause_holder::modal_pause_holder()
413 platform::set_modal_pause(true);
416 modal_pause_holder::~modal_pause_holder()
418 platform::set_modal_pause(false);
421 bool platform::pausing_allowed = true;
422 double platform::global_volume = 1.0;
423 volatile bool queue_synchronous_fn_warning;