Use C++11 isms new in G++ 4.6.
[lsnes.git] / SDL / window-sdl.cpp
blobe19a4f7bf13d674db75e669610817aab1e176697
1 #include "window.hpp"
2 #include "render.hpp"
3 #include "command.hpp"
4 #include "framerate.hpp"
5 #include "misc.hpp"
6 #include "lsnes.hpp"
7 #include "settings.hpp"
8 #include <vector>
9 #include <iostream>
10 #include <csignal>
11 #include "keymapper.hpp"
12 #include "framerate.hpp"
13 #include <sstream>
14 #include <fstream>
15 #include <cassert>
17 #define WATCHDOG_TIMEOUT 15
18 #define MAXMESSAGES 6
19 #define MSGHISTORY 1000
20 #define MAXHISTORY 1000
22 #include <SDL.h>
23 #include <string>
24 #include <map>
25 #include <stdexcept>
27 // Limit the emulator to ~30fps.
28 #define MIN_UPDATE_TIME 33333
31 namespace
33 bool wait_canceled;
34 SDL_TimerID tid;
36 void sigalrm_handler(int s)
38 _exit(1);
41 Uint32 timer_cb(Uint32 interval, void* param)
43 SDL_Event e;
44 e.type = SDL_USEREVENT;
45 e.user.code = 0;
46 SDL_PushEvent(&e);
47 return interval;
50 struct sdl_modifier
52 const char* name;
53 const char* linkname;
54 unsigned sdlvalue;
55 } modifiers_table[] = {
56 { "ctrl", NULL, 0 },
57 { "lctrl", "ctrl", KMOD_LCTRL },
58 { "rctrl", "ctrl", KMOD_RCTRL },
59 { "alt", NULL, 0 },
60 { "lalt", "alt", KMOD_LALT },
61 { "ralt", "alt", KMOD_RALT },
62 { "shift", NULL, 0 },
63 { "lshift", "shift", KMOD_LSHIFT },
64 { "rshift", "shift", KMOD_RSHIFT },
65 { "meta", NULL, 0 },
66 { "lmeta", "meta", KMOD_LMETA },
67 { "rmeta", "meta", KMOD_RMETA },
68 { "num", NULL, KMOD_NUM },
69 { "caps", NULL, KMOD_CAPS },
70 { "mode", NULL, KMOD_MODE },
71 { NULL, NULL, 0 }
74 struct sdl_key
76 const char* name;
77 unsigned symbol;
78 } keys_table[] = {
79 {"backspace", SDLK_BACKSPACE },
80 {"tab", SDLK_TAB },
81 {"clear", SDLK_CLEAR },
82 {"return", SDLK_RETURN },
83 {"pause", SDLK_PAUSE },
84 {"escape", SDLK_ESCAPE },
85 {"space", SDLK_SPACE },
86 {"exclaim", SDLK_EXCLAIM },
87 {"quotedbl", SDLK_QUOTEDBL },
88 {"hash", SDLK_HASH },
89 {"dollar", SDLK_DOLLAR },
90 {"ampersand", SDLK_AMPERSAND },
91 {"quote", SDLK_QUOTE },
92 {"leftparen", SDLK_LEFTPAREN },
93 {"rightparen", SDLK_RIGHTPAREN },
94 {"asterisk", SDLK_ASTERISK },
95 {"plus", SDLK_PLUS },
96 {"comma", SDLK_COMMA },
97 {"minus", SDLK_MINUS },
98 {"period", SDLK_PERIOD },
99 {"slash", SDLK_SLASH },
100 {"0", SDLK_0 },
101 {"1", SDLK_1 },
102 {"2", SDLK_2 },
103 {"3", SDLK_3 },
104 {"4", SDLK_4 },
105 {"5", SDLK_5 },
106 {"6", SDLK_6 },
107 {"7", SDLK_7 },
108 {"8", SDLK_8 },
109 {"9", SDLK_9 },
110 {"colon", SDLK_COLON },
111 {"semicolon", SDLK_SEMICOLON },
112 {"less", SDLK_LESS },
113 {"equals", SDLK_EQUALS },
114 {"greater", SDLK_GREATER },
115 {"question", SDLK_QUESTION },
116 {"at", SDLK_AT },
117 {"leftbracket", SDLK_LEFTBRACKET },
118 {"backslash", SDLK_BACKSLASH },
119 {"rightbracket", SDLK_RIGHTBRACKET },
120 {"caret", SDLK_CARET },
121 {"underscore", SDLK_UNDERSCORE },
122 {"backquote", SDLK_BACKQUOTE },
123 {"a", SDLK_a },
124 {"b", SDLK_b },
125 {"c", SDLK_c },
126 {"d", SDLK_d },
127 {"e", SDLK_e },
128 {"f", SDLK_f },
129 {"g", SDLK_g },
130 {"h", SDLK_h },
131 {"i", SDLK_i },
132 {"j", SDLK_j },
133 {"k", SDLK_k },
134 {"l", SDLK_l },
135 {"m", SDLK_m },
136 {"n", SDLK_n },
137 {"o", SDLK_o },
138 {"p", SDLK_p },
139 {"q", SDLK_q },
140 {"r", SDLK_r },
141 {"s", SDLK_s },
142 {"t", SDLK_t },
143 {"u", SDLK_u },
144 {"v", SDLK_v },
145 {"w", SDLK_w },
146 {"x", SDLK_x },
147 {"y", SDLK_y },
148 {"z", SDLK_z },
149 {"delete", SDLK_DELETE },
150 {"world_0", SDLK_WORLD_0 },
151 {"world_1", SDLK_WORLD_1 },
152 {"world_2", SDLK_WORLD_2 },
153 {"world_3", SDLK_WORLD_3 },
154 {"world_4", SDLK_WORLD_4 },
155 {"world_5", SDLK_WORLD_5 },
156 {"world_6", SDLK_WORLD_6 },
157 {"world_7", SDLK_WORLD_7 },
158 {"world_8", SDLK_WORLD_8 },
159 {"world_9", SDLK_WORLD_9 },
160 {"world_10", SDLK_WORLD_10 },
161 {"world_11", SDLK_WORLD_11 },
162 {"world_12", SDLK_WORLD_12 },
163 {"world_13", SDLK_WORLD_13 },
164 {"world_14", SDLK_WORLD_14 },
165 {"world_15", SDLK_WORLD_15 },
166 {"world_16", SDLK_WORLD_16 },
167 {"world_17", SDLK_WORLD_17 },
168 {"world_18", SDLK_WORLD_18 },
169 {"world_19", SDLK_WORLD_19 },
170 {"world_20", SDLK_WORLD_20 },
171 {"world_21", SDLK_WORLD_21 },
172 {"world_22", SDLK_WORLD_22 },
173 {"world_23", SDLK_WORLD_23 },
174 {"world_24", SDLK_WORLD_24 },
175 {"world_25", SDLK_WORLD_25 },
176 {"world_26", SDLK_WORLD_26 },
177 {"world_27", SDLK_WORLD_27 },
178 {"world_28", SDLK_WORLD_28 },
179 {"world_29", SDLK_WORLD_29 },
180 {"world_30", SDLK_WORLD_30 },
181 {"world_31", SDLK_WORLD_31 },
182 {"world_32", SDLK_WORLD_32 },
183 {"world_33", SDLK_WORLD_33 },
184 {"world_34", SDLK_WORLD_34 },
185 {"world_35", SDLK_WORLD_35 },
186 {"world_36", SDLK_WORLD_36 },
187 {"world_37", SDLK_WORLD_37 },
188 {"world_38", SDLK_WORLD_38 },
189 {"world_39", SDLK_WORLD_39 },
190 {"world_40", SDLK_WORLD_40 },
191 {"world_41", SDLK_WORLD_41 },
192 {"world_42", SDLK_WORLD_42 },
193 {"world_43", SDLK_WORLD_43 },
194 {"world_44", SDLK_WORLD_44 },
195 {"world_45", SDLK_WORLD_45 },
196 {"world_46", SDLK_WORLD_46 },
197 {"world_47", SDLK_WORLD_47 },
198 {"world_48", SDLK_WORLD_48 },
199 {"world_49", SDLK_WORLD_49 },
200 {"world_50", SDLK_WORLD_50 },
201 {"world_51", SDLK_WORLD_51 },
202 {"world_52", SDLK_WORLD_52 },
203 {"world_53", SDLK_WORLD_53 },
204 {"world_54", SDLK_WORLD_54 },
205 {"world_55", SDLK_WORLD_55 },
206 {"world_56", SDLK_WORLD_56 },
207 {"world_57", SDLK_WORLD_57 },
208 {"world_58", SDLK_WORLD_58 },
209 {"world_59", SDLK_WORLD_59 },
210 {"world_60", SDLK_WORLD_60 },
211 {"world_61", SDLK_WORLD_61 },
212 {"world_62", SDLK_WORLD_62 },
213 {"world_63", SDLK_WORLD_63 },
214 {"world_64", SDLK_WORLD_64 },
215 {"world_65", SDLK_WORLD_65 },
216 {"world_66", SDLK_WORLD_66 },
217 {"world_67", SDLK_WORLD_67 },
218 {"world_68", SDLK_WORLD_68 },
219 {"world_69", SDLK_WORLD_69 },
220 {"world_70", SDLK_WORLD_70 },
221 {"world_71", SDLK_WORLD_71 },
222 {"world_72", SDLK_WORLD_72 },
223 {"world_73", SDLK_WORLD_73 },
224 {"world_74", SDLK_WORLD_74 },
225 {"world_75", SDLK_WORLD_75 },
226 {"world_76", SDLK_WORLD_76 },
227 {"world_77", SDLK_WORLD_77 },
228 {"world_78", SDLK_WORLD_78 },
229 {"world_79", SDLK_WORLD_79 },
230 {"world_80", SDLK_WORLD_80 },
231 {"world_81", SDLK_WORLD_81 },
232 {"world_82", SDLK_WORLD_82 },
233 {"world_83", SDLK_WORLD_83 },
234 {"world_84", SDLK_WORLD_84 },
235 {"world_85", SDLK_WORLD_85 },
236 {"world_86", SDLK_WORLD_86 },
237 {"world_87", SDLK_WORLD_87 },
238 {"world_88", SDLK_WORLD_88 },
239 {"world_89", SDLK_WORLD_89 },
240 {"world_90", SDLK_WORLD_90 },
241 {"world_91", SDLK_WORLD_91 },
242 {"world_92", SDLK_WORLD_92 },
243 {"world_93", SDLK_WORLD_93 },
244 {"world_94", SDLK_WORLD_94 },
245 {"world_95", SDLK_WORLD_95 },
246 {"kp0", SDLK_KP0 },
247 {"kp1", SDLK_KP1 },
248 {"kp2", SDLK_KP2 },
249 {"kp3", SDLK_KP3 },
250 {"kp4", SDLK_KP4 },
251 {"kp5", SDLK_KP5 },
252 {"kp6", SDLK_KP6 },
253 {"kp7", SDLK_KP7 },
254 {"kp8", SDLK_KP8 },
255 {"kp9", SDLK_KP9 },
256 {"kp_period", SDLK_KP_PERIOD },
257 {"kp_divide", SDLK_KP_DIVIDE },
258 {"kp_multiply", SDLK_KP_MULTIPLY },
259 {"kp_minus", SDLK_KP_MINUS },
260 {"kp_plus", SDLK_KP_PLUS },
261 {"kp_enter", SDLK_KP_ENTER },
262 {"kp_equals", SDLK_KP_EQUALS },
263 {"up", SDLK_UP },
264 {"down", SDLK_DOWN },
265 {"right", SDLK_RIGHT },
266 {"left", SDLK_LEFT },
267 {"insert", SDLK_INSERT },
268 {"home", SDLK_HOME },
269 {"end", SDLK_END },
270 {"pageup", SDLK_PAGEUP },
271 {"pagedown", SDLK_PAGEDOWN },
272 {"f1", SDLK_F1 },
273 {"f2", SDLK_F2 },
274 {"f3", SDLK_F3 },
275 {"f4", SDLK_F4 },
276 {"f5", SDLK_F5 },
277 {"f6", SDLK_F6 },
278 {"f7", SDLK_F7 },
279 {"f8", SDLK_F8 },
280 {"f9", SDLK_F9 },
281 {"f10", SDLK_F10 },
282 {"f11", SDLK_F11 },
283 {"f12", SDLK_F12 },
284 {"f13", SDLK_F13 },
285 {"f14", SDLK_F14 },
286 {"f15", SDLK_F15 },
287 {"numlock", SDLK_NUMLOCK },
288 {"capslock", SDLK_CAPSLOCK },
289 {"scrollock", SDLK_SCROLLOCK },
290 {"rshift", SDLK_RSHIFT },
291 {"lshift", SDLK_LSHIFT },
292 {"rctrl", SDLK_RCTRL },
293 {"lctrl", SDLK_LCTRL },
294 {"ralt", SDLK_RALT },
295 {"lalt", SDLK_LALT },
296 {"rmeta", SDLK_RMETA },
297 {"lmeta", SDLK_LMETA },
298 {"lsuper", SDLK_LSUPER },
299 {"rsuper", SDLK_RSUPER },
300 {"mode", SDLK_MODE },
301 {"compose", SDLK_COMPOSE },
302 {"help", SDLK_HELP },
303 {"print", SDLK_PRINT },
304 {"sysreq", SDLK_SYSREQ },
305 {"break", SDLK_BREAK },
306 {"menu", SDLK_MENU },
307 {"power", SDLK_POWER },
308 {"euro", SDLK_EURO },
309 {"undo", SDLK_UNDO },
310 {NULL, 0 }
313 std::map<unsigned, modifier*> supported_modifiers;
314 std::map<unsigned, keygroup*> scancodekeys;
315 std::map<unsigned, keygroup*> symbolkeys;
316 std::map<unsigned, keygroup*> joyaxis;
317 std::map<unsigned, keygroup*> joybutton;
318 std::map<unsigned, keygroup*> joyhat;
320 void init_keys()
322 struct sdl_modifier* m = modifiers_table;
323 while(m->name) {
324 modifier* m2;
325 if(m->linkname)
326 m2 = new modifier(m->name, m->linkname);
327 else
328 m2 = new modifier(m->name);
329 if(m->sdlvalue)
330 supported_modifiers[m->sdlvalue] = m2;
331 m++;
333 struct sdl_key* k = keys_table;
334 while(k->name) {
335 symbolkeys[k->symbol] = new keygroup(k->name, keygroup::KT_KEY);
336 k++;
338 for(unsigned i = 0; i < 256; i++) {
339 std::ostringstream x;
340 x << "key" << i;
341 scancodekeys[i] = new keygroup(x.str(), keygroup::KT_KEY);
345 void init_joysticks()
347 int joysticks = SDL_NumJoysticks();
348 if(!joysticks) {
349 window::out() << "No joysticks detected." << std::endl;
350 } else {
351 window::out() << joysticks << " joystick(s) detected." << std::endl;
352 for(int i = 0; i < joysticks; i++) {
353 SDL_Joystick* j = SDL_JoystickOpen(i);
354 if(!j) {
355 window::out() << "Joystick #" << i << ": Can't open!" << std::endl;
356 continue;
358 window::out() << "Joystick #" << i << ": " << SDL_JoystickName(i) << "("
359 << SDL_JoystickNumAxes(j) << " axes, " << SDL_JoystickNumButtons(j)
360 << " buttons, " << SDL_JoystickNumHats(j) << " hats)." << std::endl;
361 for(int k = 0; k < SDL_JoystickNumAxes(j); k++) {
362 unsigned num = 256 * i + k;
363 std::ostringstream x;
364 x << "joystick" << i << "axis" << k;
365 joyaxis[num] = new keygroup(x.str(), keygroup::KT_AXIS_PAIR);
367 for(int k = 0; k < SDL_JoystickNumButtons(j); k++) {
368 unsigned num = 256 * i + k;
369 std::ostringstream x;
370 x << "joystick" << i << "button" << k;
371 joybutton[num] = new keygroup(x.str(), keygroup::KT_KEY);
373 for(int k = 0; k < SDL_JoystickNumHats(j); k++) {
374 unsigned num = 256 * i + k;
375 std::ostringstream x;
376 x << "joystick" << i << "hat" << k;
377 joyhat[num] = new keygroup(x.str(), keygroup::KT_HAT);
383 struct identify_helper : public keygroup::key_listener
385 void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey,
386 bool polarity, const std::string& name)
388 if(!polarity)
389 _keys = _keys + "Name: " + name + "\n";
391 bool got_it()
393 return (_keys != "");
395 std::string keys()
397 return _keys;
399 std::string _keys;
402 struct key_eater : public keygroup::key_listener
404 void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey,
405 bool polarity, const std::string& name)
407 //Just eat it.
409 } keyeater;
411 void process_input_event(SDL_Event* e, bool identify)
413 identify_helper h;
414 if(identify)
415 keygroup::set_exclusive_key_listener(&h);
416 modifier_set modifiers;
417 if(e->type == SDL_KEYDOWN || e->type == SDL_KEYUP) {
418 SDL_keysym sym = e->key.keysym;
419 uint8_t scancode = sym.scancode;
420 unsigned symbol = sym.sym;
421 for(auto k : supported_modifiers)
422 if(sym.mod & k.first)
423 modifiers.add(*k.second);
424 scancodekeys[scancode]->set_position((e->type == SDL_KEYDOWN) ? 1 : 0, modifiers);
425 if(symbolkeys.count(symbol))
426 symbolkeys[symbol]->set_position((e->type == SDL_KEYDOWN) ? 1 : 0, modifiers);
427 } else if(e->type == SDL_JOYAXISMOTION) {
428 unsigned num = static_cast<unsigned>(e->jaxis.which) * 256 +
429 static_cast<unsigned>(e->jaxis.axis);
430 if(joyaxis.count(num))
431 joyaxis[num]->set_position(e->jaxis.value, modifiers);
432 } else if(e->type == SDL_JOYHATMOTION) {
433 unsigned num = static_cast<unsigned>(e->jhat.which) * 256 +
434 static_cast<unsigned>(e->jhat.hat);
435 short v = 0;
436 if(e->jhat.value & SDL_HAT_UP)
437 v |= 1;
438 if(e->jhat.value & SDL_HAT_RIGHT)
439 v |= 2;
440 if(e->jhat.value & SDL_HAT_DOWN)
441 v |= 4;
442 if(e->jhat.value & SDL_HAT_LEFT)
443 v |= 8;
444 if(joyhat.count(num))
445 joyhat[num]->set_position(v, modifiers);
446 } else if(e->type == SDL_JOYBUTTONDOWN || e->type == SDL_JOYBUTTONUP) {
447 unsigned num = static_cast<unsigned>(e->jbutton.which) * 256 +
448 static_cast<unsigned>(e->jbutton.button);
449 if(joybutton.count(num))
450 joybutton[num]->set_position((e->type == SDL_JOYBUTTONDOWN), modifiers);
452 if(identify) {
453 if(h.got_it())
454 window::modal_message(h.keys(), false);
455 keygroup::set_exclusive_key_listener(NULL);
460 extern uint32_t fontdata[];
462 namespace
464 bool SDL_initialized = false;
465 uint32_t mouse_mask = 0;
466 uint32_t vc_xoffset;
467 uint32_t vc_yoffset;
468 uint32_t vc_hscl = 1;
469 uint32_t vc_vscl = 1;
470 bool sdl_init;
471 bool modconfirm;
472 bool modal_return_flag;
473 bool delayed_close_flag;
474 std::string modmsg;
475 std::string command_buf;
476 bool command_overwrite;
477 size_t command_cursor;
478 unsigned old_screen_w;
479 unsigned old_screen_h;
480 unsigned state;
481 std::map<std::string, std::string> emustatus;
482 std::map<uint64_t, std::string> messagebuffer;
483 uint64_t messagebuffer_next_seq;
484 uint64_t messagebuffer_first_seq;
485 uint64_t messagebuffer_first_show;
486 bool console_mode;
487 uint32_t maxmessages;
488 std::list<std::string> commandhistory;
489 std::list<std::string>::iterator commandhistory_itr;
490 screen* current_screen;
491 SDL_Surface* hwsurf;
492 SDL_Surface* swsurf;
493 std::pair<uint32_t, uint32_t> current_windowsize;
494 bool pause_active;
495 uint64_t last_ui_update;
496 bool screen_is_dirty;
497 std::ofstream system_log;
498 SDL_keysym autorepeating_key;
499 unsigned autorepeat_phase = 0;
500 unsigned autorepeat_timecounter = 0;
501 numeric_setting autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
502 numeric_setting autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
505 void poll_inputs_internal() throw(std::bad_alloc);
507 namespace
509 const size_t audiobuf_size = 8192;
510 uint16_t audiobuf[audiobuf_size];
511 volatile size_t audiobuf_get = 0;
512 volatile size_t audiobuf_put = 0;
513 uint64_t sampledup_ctr = 0;
514 uint64_t sampledup_inc = 0;
515 uint64_t sampledup_mod = 1;
516 Uint16 format = AUDIO_S16SYS;
517 bool stereo = true;
518 bool sound_enabled = true;
520 void calculate_sampledup(uint32_t real_rate)
522 sampledup_ctr = 0;
523 sampledup_inc = 64081;
524 sampledup_mod = 2 * real_rate + 64081;
527 void audiocb(void* dummy, Uint8* stream, int len)
529 static uint16_t lprev = 32768;
530 static uint16_t rprev = 32768;
531 if(!sound_enabled)
532 lprev = rprev = 32768;
533 uint16_t bias = (format == AUDIO_S8 || format == AUDIO_S16LSB || format == AUDIO_S16MSB || format ==
534 AUDIO_S16SYS) ? 32768 : 0;
535 while(len > 0) {
536 uint16_t l, r;
537 if(audiobuf_get == audiobuf_put) {
538 l = lprev;
539 r = rprev;
540 } else {
541 l = lprev = audiobuf[audiobuf_get++];
542 r = rprev = audiobuf[audiobuf_get++];
543 if(audiobuf_get == audiobuf_size)
544 audiobuf_get = 0;
546 if(!stereo)
547 l = l / 2 + r / 2;
548 if(format == AUDIO_U8 || format == AUDIO_S8) {
549 stream[0] = (l - bias) >> 8;
550 if(stereo)
551 stream[1] = (r - bias) >> 8;
552 stream += (stereo ? 2 : 1);
553 len -= (stereo ? 2 : 1);
554 } else if(format == AUDIO_S16SYS || format == AUDIO_U16SYS) {
555 reinterpret_cast<uint16_t*>(stream)[0] = (l - bias);
556 if(stereo)
557 reinterpret_cast<int16_t*>(stream)[1] = (r - bias);
558 stream += (stereo ? 4 : 2);
559 len -= (stereo ? 4 : 2);
560 } else if(format == AUDIO_S16LSB || format == AUDIO_U16LSB) {
561 stream[0] = (l - bias);
562 stream[1] = (l - bias) >> 8;
563 if(stereo) {
564 stream[2] = (r - bias);
565 stream[3] = (r - bias) >> 8;
567 stream += (stereo ? 4 : 2);
568 len -= (stereo ? 4 : 2);
569 } else if(format == AUDIO_S16MSB || format == AUDIO_U16MSB) {
570 stream[1] = (l - bias);
571 stream[0] = (l - bias) >> 8;
572 if(stereo) {
573 stream[3] = (r - bias);
574 stream[2] = (r - bias) >> 8;
576 stream += (stereo ? 4 : 2);
577 len -= (stereo ? 4 : 2);
582 void identify()
584 state = WINSTATE_IDENTIFY;
585 window::message("Press key to identify.");
586 window::notify_screen_update();
587 poll_inputs_internal();
590 std::string decode_string(std::string e)
592 std::string x;
593 for(size_t i = 0; i < e.length(); i += 4) {
594 char tmp[5] = {0};
595 uint32_t c1 = e[i] - 33;
596 uint32_t c2 = e[i + 1] - 33;
597 uint32_t c3 = e[i + 2] - 33;
598 uint32_t c4 = e[i + 3] - 33;
599 uint32_t c = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
600 if(c < 0x80) {
601 tmp[0] = c;
602 } else if(c < 0x800) {
603 tmp[0] = 0xC0 | (c >> 6);
604 tmp[1] = 0x80 | (c & 0x3F);
605 } else if(c < 0x10000) {
606 tmp[0] = 0xE0 | (c >> 12);
607 tmp[1] = 0x80 | ((c >> 6) & 0x3F);
608 tmp[2] = 0x80 | (c & 0x3F);
609 } else {
610 tmp[0] = 0xF0 | (c >> 18);
611 tmp[1] = 0x80 | ((c >> 12) & 0x3F);
612 tmp[2] = 0x80 | ((c >> 6) & 0x3F);
613 tmp[3] = 0x80 | (c & 0x3F);
615 x = x + tmp;
617 return x;
620 void draw_rectangle(uint8_t* data, uint32_t pitch, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
621 uint32_t color, uint32_t thickness)
623 for(uint32_t i = x1; i < x2; i++)
624 for(uint32_t j = 0; j < thickness; j++) {
625 reinterpret_cast<uint32_t*>(data + pitch * (y1 + j))[i] = color;
626 reinterpret_cast<uint32_t*>(data + pitch * (y2 - 1 - j))[i] = color;
628 for(uint32_t i = y1; i < y2; i++)
629 for(uint32_t j = 0; j < thickness; j++) {
630 reinterpret_cast<uint32_t*>(data + pitch * i)[x1 + j] = color;
631 reinterpret_cast<uint32_t*>(data + pitch * i)[x2 - 1 - j] = color;
635 std::vector<uint32_t> decode_utf8(std::string s)
637 std::vector<uint32_t> ret;
638 for(auto i = s.begin(); i != s.end(); i++) {
639 uint32_t j = static_cast<uint8_t>(*i);
640 if(j < 128)
641 ret.push_back(j);
642 else if(j < 192)
643 continue;
644 else if(j < 224) {
645 uint32_t j2 = static_cast<uint8_t>(*(++i));
646 ret.push_back((j - 192) * 64 + (j2 - 128));
647 } else if(j < 240) {
648 uint32_t j2 = static_cast<uint8_t>(*(++i));
649 uint32_t j3 = static_cast<uint8_t>(*(++i));
650 ret.push_back((j - 224) * 4096 + (j2 - 128) * 64 + (j3 - 128));
651 } else {
652 uint32_t j2 = static_cast<uint8_t>(*(++i));
653 uint32_t j3 = static_cast<uint8_t>(*(++i));
654 uint32_t j4 = static_cast<uint8_t>(*(++i));
655 ret.push_back((j - 240) * 262144 + (j2 - 128) * 4096 + (j3 - 128) * 64 + (j4 - 128));
658 return ret;
661 void draw_string(uint8_t* base, uint32_t pitch, std::vector<uint32_t> s, uint32_t x, uint32_t y,
662 uint32_t maxwidth, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
664 base += y * static_cast<size_t>(pitch) + 4 * x;
665 int32_t pos_x = 0;
666 int32_t pos_y = 0;
667 unsigned c = 0;
668 for(auto si : s) {
669 uint32_t old_x = pos_x;
670 uint32_t curstart = 16;
671 if(c == hilite_pos && hilite_mode == 1)
672 curstart = 14;
673 if(c == hilite_pos && hilite_mode == 2)
674 curstart = 0;
675 auto g = find_glyph(si, pos_x, pos_y, 0, pos_x, pos_y);
676 if(pos_y)
677 pos_x = old_x;
678 if(g.second == 0) {
679 //Empty glyph.
680 for(unsigned j = 0; j < 16; j++) {
681 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
682 for(unsigned i = 0; i < g.first && old_x + i < maxwidth; i++)
683 ptr[old_x + i] = (j >= curstart) ? 0xFFFFFFU : 0;
685 } else {
686 //Narrow/Wide glyph.
687 for(unsigned j = 0; j < 16; j++) {
688 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
689 uint32_t dataword = fontdata[g.second + j / 4];
690 for(uint32_t i = 0; i < g.first && old_x + i < maxwidth; i++) {
691 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
692 1));
693 b ^= (j >= curstart);
694 ptr[old_x + i] = b ? 0xFFFFFFU : 0;
698 c++;
700 for(unsigned j = 0; j < 16; j++) {
701 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
702 uint32_t curstart = 16;
703 if(c == hilite_pos && hilite_mode == 1)
704 curstart = 14;
705 if(c == hilite_pos && hilite_mode == 2)
706 curstart = 0;
707 for(uint32_t i = pos_x; i < maxwidth; i++)
708 ptr[i] = ((i - pos_x) < 8 && j >= curstart) ? 0xFFFFFFU : 0;
712 void draw_string(uint8_t* base, uint32_t pitch, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth,
713 uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
715 draw_string(base, pitch, decode_utf8(s), x, y, maxwidth, hilite_mode, hilite_pos);
718 void draw_command(uint8_t* base, uint32_t pitch, std::string s, size_t cursor, uint32_t x, uint32_t y,
719 uint32_t maxwidth, bool overwrite)
721 //FIXME, scroll text if too long.
722 uint32_t hilite_mode = overwrite ? 2 : 1;
723 auto s2 = decode_utf8(s);
724 draw_string(base, pitch, s2, x, y, maxwidth, hilite_mode, cursor);
727 void draw_modal_dialog(SDL_Surface* surf, std::string msg, bool confirm)
729 int32_t pos_x = 0;
730 int32_t pos_y = 0;
731 uint32_t width = 0;
732 uint32_t height = 0;
733 if(confirm)
734 msg = msg + "\n\nHit Enter to confirm, Esc to cancel";
735 else
736 msg = msg + "\n\nHit Enter or Esc to dismiss";
737 auto s2 = decode_utf8(msg);
738 for(auto i : s2) {
739 auto g = find_glyph(i, pos_x, pos_y, 0, pos_x, pos_y);
740 if(pos_x + g.first > width)
741 width = static_cast<uint32_t>(pos_x + g.first);
742 if(pos_y + 16 > static_cast<int32_t>(height))
743 height = static_cast<uint32_t>(pos_y + 16);
745 uint32_t x1;
746 uint32_t x2;
747 uint32_t y1;
748 uint32_t y2;
749 if(width + 12 >= static_cast<uint32_t>(surf->w)) {
750 x1 = 6;
751 x2 = surf->w - 6;
752 width = x2 - x1;
753 } else {
754 x1 = (surf->w - width) / 2;
755 x2 = x1 + width;
757 if(height + 12 >= static_cast<uint32_t>(surf->h)) {
758 y1 = 6;
759 y2 = surf->h - 6;
760 height = y2 - y1;
761 } else {
762 y1 = (surf->h - height) / 2;
763 y2 = y1 + height;
765 for(uint32_t j = y1 - 6; j < y2 + 6; j++)
766 memset(reinterpret_cast<uint8_t*>(surf->pixels) + j * surf->pitch + 4 * (x1 - 6), 0,
767 4 * (x2 - x1 + 12));
768 uint32_t bordercolor = 0xFF8000;
769 draw_rectangle(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, x1 - 4, y1 - 4, x2 + 4, y2 + 4,
770 bordercolor, 2);
772 pos_x = 0;
773 pos_y = 0;
774 for(auto i : s2) {
775 uint32_t ox = pos_x;
776 uint32_t oy = pos_y;
777 auto g = find_glyph(i, pos_x, pos_y, 0, pos_x, pos_y);
778 if(static_cast<uint32_t>(pos_y) > height)
779 break;
780 uint8_t* base = reinterpret_cast<uint8_t*>(surf->pixels) + (y1 + oy) * surf->pitch +
781 4 * (x1 + ox);
782 if(g.second) {
783 //Narrow/Wide glyph.
784 for(unsigned j = 0; j < 16; j++) {
785 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + surf->pitch * j);
786 uint32_t dataword = fontdata[g.second + j / 4];
787 for(uint32_t i = 0; i < g.first && (ox + i) < width; i++) {
788 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
789 1));
790 ptr[i] = b ? bordercolor : 0;
797 void do_keyboard_command_edit(SDL_keysym k)
799 //These are not command edit!
800 if(k.sym == SDLK_ESCAPE)
801 return;
802 if(k.sym == SDLK_RETURN)
803 return;
804 if(k.sym == SDLK_KP_ENTER)
805 return;
806 //Map keys a bit if numlock is off.
807 if((k.mod & KMOD_NUM) == 0) {
808 switch(k.sym) {
809 case SDLK_KP0: k.sym = SDLK_INSERT; break;
810 case SDLK_KP1: k.sym = SDLK_END; break;
811 case SDLK_KP2: k.sym = SDLK_DOWN; break;
812 case SDLK_KP3: k.sym = SDLK_PAGEDOWN; break;
813 case SDLK_KP4: k.sym = SDLK_LEFT; break;
814 case SDLK_KP5: return;
815 case SDLK_KP6: k.sym = SDLK_RIGHT; break;
816 case SDLK_KP7: k.sym = SDLK_HOME; break;
817 case SDLK_KP8: k.sym = SDLK_UP; break;
818 case SDLK_KP9: k.sym = SDLK_PAGEUP; break;
819 case SDLK_KP_PERIOD: k.sym = SDLK_DELETE; break;
820 default:
821 break;
824 //Special editing operations.
825 switch(k.sym) {
826 case SDLK_INSERT:
827 command_overwrite = !command_overwrite;
828 window::notify_screen_update();
829 return;
830 case SDLK_END:
831 command_cursor = command_buf.length();
832 window::notify_screen_update();
833 return;
834 case SDLK_DOWN:
835 case SDLK_PAGEDOWN:
836 if(commandhistory_itr != commandhistory.begin()) {
837 commandhistory_itr--;
838 command_buf = *commandhistory_itr;
839 if(command_cursor > command_buf.length())
840 command_cursor = command_buf.length();
842 window::notify_screen_update();
843 return;
844 case SDLK_LEFT:
845 command_cursor = (command_cursor > 0) ? (command_cursor - 4) : 0;
846 window::notify_screen_update();
847 return;
848 case SDLK_RIGHT:
849 command_cursor = (command_cursor < command_buf.length()) ? (command_cursor + 4) :
850 command_buf.length();
851 window::notify_screen_update();
852 return;
853 case SDLK_HOME:
854 command_cursor = 0;
855 window::notify_screen_update();
856 return;
857 case SDLK_UP:
858 case SDLK_PAGEUP: {
859 auto tmp = commandhistory_itr;
860 if(++tmp != commandhistory.end()) {
861 commandhistory_itr++;
862 command_buf = *commandhistory_itr;
863 if(command_cursor > command_buf.length())
864 command_cursor = command_buf.length();
866 window::notify_screen_update();
867 return;
869 case SDLK_DELETE:
870 if(command_cursor < command_buf.length())
871 command_buf = command_buf.substr(0, command_cursor) +
872 command_buf.substr(command_cursor + 4);
873 window::notify_screen_update();
874 *commandhistory_itr = command_buf;
875 return;
876 case SDLK_BACKSPACE:
877 if(command_cursor > 0) {
878 command_buf = command_buf.substr(0, command_cursor - 4) +
879 command_buf.substr(command_cursor);
880 command_cursor -= 4;
882 window::notify_screen_update();
883 *commandhistory_itr = command_buf;
884 return;
885 default:
886 break;
889 //Not a special editing operation, insert/overwrite a character.
890 uint32_t code = k.unicode;
891 if(!code)
892 return;
893 uint8_t c1 = 33 + ((code >> 18) & 0x3F);
894 uint8_t c2 = 33 + ((code >> 12) & 0x3F);
895 uint8_t c3 = 33 + ((code >> 6) & 0x3F);
896 uint8_t c4 = 33 + (code & 0x3F);
897 if(command_overwrite && command_cursor < command_buf.length()) {
898 command_buf[command_cursor] = c1;
899 command_buf[command_cursor + 1] = c2;
900 command_buf[command_cursor + 2] = c3;
901 command_buf[command_cursor + 3] = c4;
902 command_cursor += 4;
903 } else {
904 std::string foo = " ";
905 foo[0] = c1;
906 foo[1] = c2;
907 foo[2] = c3;
908 foo[3] = c4;
909 command_buf = command_buf.substr(0, command_cursor) + foo + command_buf.substr(command_cursor);
910 command_cursor += 4;
912 *commandhistory_itr = command_buf;
913 window::notify_screen_update();
916 void do_event(SDL_Event& e) throw(std::bad_alloc)
918 #ifdef SIGALRM
919 alarm(WATCHDOG_TIMEOUT);
920 #endif
921 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE && e.key.keysym.mod == (KMOD_LCTRL |
922 KMOD_LALT))
923 exit(1);
924 if(e.type == SDL_USEREVENT && e.user.code == 0) {
925 if(screen_is_dirty)
926 window::notify_screen_update();
928 SDLKey key;
929 if(e.type == SDL_ACTIVEEVENT && e.active.gain && e.active.state == SDL_APPACTIVE) {
930 window::notify_screen_update();
931 return;
933 if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
934 key = e.key.keysym.sym;
936 if(e.type == SDL_QUIT && state == WINSTATE_IDENTIFY)
937 return;
938 if(e.type == SDL_QUIT && state == WINSTATE_MODAL) {
939 delayed_close_flag = true;
940 return;
942 if(e.type == SDL_QUIT) {
943 window_callback::do_close();
944 state = WINSTATE_NORMAL;
945 return;
948 switch(state) {
949 case WINSTATE_NORMAL:
950 if(e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
951 int32_t xc = e.button.x;
952 int32_t yc = e.button.y;
953 xc = (xc - 6 - vc_xoffset) / vc_hscl;
954 yc = (yc - 6 - vc_yoffset) / vc_vscl;
955 if(e.button.button == SDL_BUTTON_LEFT) {
956 if(e.button.state == SDL_PRESSED)
957 mouse_mask |= 1;
958 else
959 mouse_mask &= ~1;
961 if(e.button.button == SDL_BUTTON_MIDDLE) {
962 if(e.button.state == SDL_PRESSED)
963 mouse_mask |= 2;
964 else
965 mouse_mask &= ~2;
967 if(e.button.button == SDL_BUTTON_RIGHT) {
968 if(e.button.state == SDL_PRESSED)
969 mouse_mask |= 4;
970 else
971 mouse_mask &= ~4;
973 window_callback::do_click(xc, yc, mouse_mask);
975 if(e.type == SDL_KEYDOWN && key == SDLK_ESCAPE)
976 return;
977 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
978 state = WINSTATE_COMMAND;
979 command_buf = "";
980 command_cursor = 0;
981 commandhistory.push_front("");
982 if(commandhistory.size() > MAXHISTORY)
983 commandhistory.pop_back();
984 commandhistory_itr = commandhistory.begin();
985 window::notify_screen_update();
986 poll_inputs_internal();
987 return;
989 process_input_event(&e, false);
990 break;
991 case WINSTATE_MODAL:
992 //Send the key and eat it (prevent input from getting confused).
993 keygroup::set_exclusive_key_listener(&keyeater);
994 process_input_event(&e, false),
995 keygroup::set_exclusive_key_listener(NULL);
996 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
997 state = WINSTATE_NORMAL;
998 modconfirm = false;
999 modal_return_flag = true;
1000 modmsg = "";
1001 window::notify_screen_update(true);
1002 return;
1004 if(e.type == SDL_KEYUP && (key == SDLK_RETURN || key == SDLK_KP_ENTER)) {
1005 state = WINSTATE_NORMAL;
1006 modal_return_flag = true;
1007 modmsg = "";
1008 window::notify_screen_update(true);
1009 return;
1011 break;
1012 case WINSTATE_COMMAND:
1013 //Send the key and eat it (prevent input from getting confused).
1014 keygroup::set_exclusive_key_listener(&keyeater);
1015 process_input_event(&e, false),
1016 keygroup::set_exclusive_key_listener(NULL);
1017 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) {
1018 state = WINSTATE_NORMAL;
1019 command_buf = "";
1020 window::notify_screen_update();
1021 if(commandhistory.front() == "")
1022 commandhistory.pop_front();
1023 return;
1025 if(e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_RETURN ||
1026 e.key.keysym.sym == SDLK_KP_ENTER)) {
1027 state = WINSTATE_NORMAL;
1028 if(commandhistory.front() == "")
1029 commandhistory.pop_front();
1030 command::invokeC(decode_string(command_buf));
1031 command_buf = "";
1032 window::notify_screen_update();
1033 autorepeat_phase = 0;
1034 return;
1036 if(e.type == SDL_KEYDOWN) {
1037 autorepeating_key = e.key.keysym;
1038 autorepeat_phase = 1;
1039 autorepeat_timecounter = 0;
1040 do_keyboard_command_edit(e.key.keysym);
1041 } else if(e.type == SDL_KEYUP) {
1042 autorepeat_phase = 0;
1044 if(e.type == SDL_USEREVENT && e.user.code == 0) {
1045 autorepeat_timecounter++;
1046 if(!autorepeat_phase)
1047 break;
1048 unsigned timeout = (autorepeat_phase == 1) ? autorepeat_first : autorepeat_subsequent;
1049 if(autorepeat_timecounter >= timeout) {
1050 do_keyboard_command_edit(autorepeating_key);
1051 autorepeat_timecounter = 0;
1052 autorepeat_phase = 2;
1055 break;
1056 case WINSTATE_IDENTIFY:
1057 process_input_event(&e, true);
1058 break;
1063 void window::init()
1065 SDL_initialized = true;
1066 #ifdef SIGALRM
1067 signal(SIGALRM, sigalrm_handler);
1068 alarm(WATCHDOG_TIMEOUT);
1069 #endif
1070 init_keys();
1071 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
1072 time_t curtime = __real_time(NULL);
1073 struct tm* tm = localtime(&curtime);
1074 char buffer[1024];
1075 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1076 system_log << "-----------------------------------------------------------------------" << std::endl;
1077 system_log << "lsnes started at " << buffer << std::endl;
1078 system_log << "-----------------------------------------------------------------------" << std::endl;
1079 if(!sdl_init) {
1080 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
1081 SDL_EnableUNICODE(true);
1082 sdl_init = true;
1083 tid = SDL_AddTimer(MIN_UPDATE_TIME / 1000 + 1, timer_cb, NULL);
1085 state = WINSTATE_NORMAL;
1086 current_screen = NULL;
1087 pause_active = false;
1088 hwsurf = NULL;
1089 swsurf = NULL;
1090 command_overwrite = false;
1091 old_screen_h = 0;
1092 old_screen_w = 0;
1093 modal_return_flag = false;
1094 delayed_close_flag = false;
1095 messagebuffer_next_seq = 0;
1096 messagebuffer_first_seq = 0;
1097 messagebuffer_first_show = 0;
1098 console_mode = false;
1099 maxmessages = MAXMESSAGES;
1101 notify_screen_update();
1102 std::string windowname = "lsnes-" + lsnes_version + "[" + bsnes_core_version + "]";
1103 SDL_WM_SetCaption(windowname.c_str(), "lsnes");
1105 init_joysticks();
1107 SDL_AudioSpec* desired = new SDL_AudioSpec();
1108 SDL_AudioSpec* obtained = new SDL_AudioSpec();
1110 desired->freq = 44100;
1111 desired->format = AUDIO_S16SYS;
1112 desired->channels = 2;
1113 desired->samples = 8192;
1114 desired->callback = audiocb;
1115 desired->userdata = NULL;
1117 if(SDL_OpenAudio(desired, obtained) < 0) {
1118 message("Audio can't be initialized, audio playback disabled");
1119 //Disable audio.
1120 sampledup_ctr = 0;
1121 sampledup_inc = 0;
1122 sampledup_mod = 0;
1123 return;
1126 //Fill the parameters.
1127 calculate_sampledup(obtained->freq);
1128 format = obtained->format;
1129 stereo = (obtained->channels == 2);
1130 //GO!!!
1131 SDL_PauseAudio(0);
1134 void window::quit()
1136 time_t curtime = time(NULL);
1137 struct tm* tm = localtime(&curtime);
1138 char buffer[1024];
1139 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1140 system_log << "-----------------------------------------------------------------------" << std::endl;
1141 system_log << "lsnes shutting down at " << buffer << std::endl;
1142 system_log << "-----------------------------------------------------------------------" << std::endl;
1143 system_log.close();
1144 if(sdl_init) {
1145 SDL_RemoveTimer(tid);
1146 SDL_Quit();
1147 sdl_init = false;
1149 SDL_initialized = false;
1152 bool window::modal_message(const std::string& msg, bool confirm) throw(std::bad_alloc)
1154 modconfirm = confirm;
1155 modmsg = msg;
1156 state = WINSTATE_MODAL;
1157 notify_screen_update();
1158 poll_inputs_internal();
1159 bool ret = modconfirm;
1160 if(delayed_close_flag) {
1161 delayed_close_flag = false;
1162 window_callback::do_close();
1164 return ret;
1167 void window::message(const std::string& msg) throw(std::bad_alloc)
1169 std::string msg2 = msg;
1170 bool locked_mode = (messagebuffer_next_seq - messagebuffer_first_show <= maxmessages) ;
1171 while(msg2 != "") {
1172 size_t s = msg2.find_first_of("\n");
1173 std::string forlog;
1174 if(s >= msg2.length()) {
1175 if(SDL_initialized) {
1176 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2);
1177 system_log << forlog << std::endl;
1178 } else
1179 std::cerr << msg2 << std::endl;
1180 break;
1181 } else {
1182 if(SDL_initialized) {
1183 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2.substr(0, s));
1184 system_log << forlog << std::endl;
1185 } else
1186 std::cerr << msg2.substr(0, s) << std::endl;
1187 msg2 = msg2.substr(s + 1);
1191 if(locked_mode && messagebuffer_first_show + maxmessages < messagebuffer_next_seq)
1192 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1194 while(messagebuffer.size() > MSGHISTORY) {
1195 messagebuffer.erase(messagebuffer_first_seq++);
1196 if(messagebuffer_first_show < messagebuffer_first_seq)
1197 messagebuffer_first_show = messagebuffer_first_seq;
1199 notify_screen_update();
1202 void window::set_main_surface(screen& scr) throw()
1204 current_screen = &scr;
1205 notify_screen_update(true);
1208 namespace
1210 bool is_time_for_screen_update(bool full)
1212 uint64_t curtime = get_utime();
1213 //Always do full updates.
1214 if(!full && last_ui_update < curtime && last_ui_update + MIN_UPDATE_TIME > curtime) {
1215 screen_is_dirty = true;
1216 return false;
1218 last_ui_update = curtime;
1219 screen_is_dirty = false;
1220 return true;
1223 std::pair<uint32_t, uint32_t> compute_screen_size(uint32_t width, uint32_t height)
1225 if(width < 512)
1226 width = 512;
1227 if(height < 448)
1228 height = 448;
1229 return std::make_pair(width, height);
1232 std::pair<uint32_t, uint32_t> compute_window_size(uint32_t width, uint32_t height)
1234 auto g = compute_screen_size(width, height);
1235 uint32_t win_w = ((g.first + 15) >> 4 << 4) + 278;
1236 uint32_t win_h = g.second + MAXMESSAGES * 16 + 48;
1237 return std::make_pair(win_w, win_h);
1240 void show_fps()
1242 try {
1243 std::ostringstream y;
1244 y << get_framerate();
1245 emustatus["FPS"] = y.str();
1246 } catch(...) {
1250 void redraw_borders(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize,
1251 std::pair<uint32_t, uint32_t> windowsize)
1253 //Blank the screen and draw borders.
1254 memset(swsurf->pixels, 0, windowsize.second * swsurf->pitch);
1255 uint32_t bordercolor = 0x00FF00;
1256 uint32_t msgbox_min_x = 2;
1257 uint32_t msgbox_min_y = 2;
1258 uint32_t msgbox_max_x = windowsize.first - 2;
1259 uint32_t msgbox_max_y = windowsize.second - 28;
1260 uint32_t cmdbox_min_x = 2;
1261 uint32_t cmdbox_max_x = windowsize.first - 2;
1262 uint32_t cmdbox_min_y = windowsize.second - 26;
1263 uint32_t cmdbox_max_y = windowsize.second - 2;
1264 if(!console_mode) {
1265 uint32_t scrbox_min_x = 2;
1266 uint32_t scrbox_max_x = screensize.first + 10;
1267 uint32_t scrbox_min_y = 2;
1268 uint32_t scrbox_max_y = screensize.second + 10;
1269 uint32_t stsbox_min_x = screensize.first + 12;
1270 uint32_t stsbox_max_x = windowsize.first - 2;
1271 uint32_t stsbox_min_y = 2;
1272 uint32_t stsbox_max_y = screensize.second + 10;
1273 msgbox_min_y = screensize.second + 12;
1274 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, scrbox_min_x,
1275 scrbox_min_y, scrbox_max_x, scrbox_max_y, bordercolor, 2);
1276 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, stsbox_min_x,
1277 stsbox_min_y, stsbox_max_x, stsbox_max_y, bordercolor, 2);
1279 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, msgbox_min_x,
1280 msgbox_min_y, msgbox_max_x, msgbox_max_y, bordercolor, 2);
1281 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, cmdbox_min_x,
1282 cmdbox_min_y, cmdbox_max_x, cmdbox_max_y, bordercolor, 2);
1285 void draw_main_screen(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize)
1287 uint32_t cw = current_screen ? current_screen->width : 0;
1288 uint32_t ch = current_screen ? current_screen->height : 0;
1289 //Blank parts not drawn.
1290 for(uint32_t i = 6; i < ch + 6; i++)
1291 memset(reinterpret_cast<uint8_t*>(swsurf->pixels) + i * swsurf->pitch + 24 + 4 * cw, 0,
1292 4 * (screensize.first - cw));
1293 for(uint32_t i = ch + 6; i < screensize.second + 6; i++)
1294 memset(reinterpret_cast<uint8_t*>(swsurf->pixels) + i * swsurf->pitch + 24, 0,
1295 4 * screensize.first);
1296 if(current_screen) {
1297 for(uint32_t i = 0; i < ch; i++)
1298 memcpy(reinterpret_cast<uint8_t*>(swsurf->pixels) + (i + 6) * swsurf->pitch + 24,
1299 reinterpret_cast<uint8_t*>(current_screen->memory) + current_screen->pitch * i,
1300 4 * cw);
1304 void draw_status_area(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize)
1306 uint32_t status_x = screensize.first + 16;
1307 uint32_t status_y = 6;
1308 for(auto i : emustatus) {
1309 std::string msg = i.first + " " + i.second;
1310 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, msg, status_x, status_y,
1311 256);
1312 status_y += 16;
1314 while(status_y - 6 < screensize.second / 16 * 16) {
1315 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "", status_x, status_y,
1316 256);
1317 status_y += 16;
1321 void draw_messages(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize,
1322 std::pair<uint32_t, uint32_t> windowsize)
1324 uint32_t message_y;
1325 if(!console_mode)
1326 message_y = screensize.second + 16;
1327 else
1328 message_y = 6;
1329 for(size_t j = 0; j < maxmessages; j++)
1330 try {
1331 std::ostringstream o;
1332 if(messagebuffer_first_show + j < messagebuffer_next_seq)
1333 o << (messagebuffer_first_show + j + 1) << ": "
1334 << messagebuffer[messagebuffer_first_show + j];
1335 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, o.str(), 6,
1336 message_y + 16 * j, windowsize.first - 12);
1337 } catch(...) {
1339 if(messagebuffer_next_seq - messagebuffer_first_show > maxmessages)
1340 try {
1341 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "--More--",
1342 windowsize.first - 76, message_y + 16 * maxmessages - 16, 64);
1343 } catch(...) {
1347 void draw_command(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> windowsize)
1349 uint32_t command_y = windowsize.second - 22;
1350 try {
1351 std::string command_showas = decode_string(command_buf);
1352 if(state == WINSTATE_COMMAND)
1353 draw_command(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, command_showas,
1354 command_cursor / 4, 6, command_y, windowsize.first - 12, command_overwrite);
1355 else
1356 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "", 6,
1357 command_y, windowsize.first - 12);
1358 } catch(...) {
1363 void window::notify_screen_update(bool full) throw()
1365 bool resize_screen = false;
1366 if(!is_time_for_screen_update(full))
1367 return;
1368 auto windowsize = compute_window_size(current_screen ? current_screen->width : 0, current_screen ?
1369 current_screen->height : 0);
1370 auto screensize = compute_screen_size(current_screen ? current_screen->width : 0, current_screen ?
1371 current_screen->height : 0);
1372 show_fps();
1375 if(!hwsurf || windowsize != current_windowsize) {
1376 //Create/Resize the window.
1377 SDL_Surface* hwsurf2 = SDL_SetVideoMode(windowsize.first, windowsize.second, 0, SDL_SWSURFACE |
1378 SDL_ANYFORMAT);
1379 SDL_Surface* swsurf2 = SDL_CreateRGBSurface(SDL_SWSURFACE, windowsize.first, windowsize.second, 32,
1380 0xFF0000, 0x00FF00, 0x0000FF, 0);
1381 if(!hwsurf2 || !swsurf2) {
1382 //We are in too fucked up state to even print error as message.
1383 std::cout << "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl;
1384 exit(1);
1386 if(swsurf)
1387 SDL_FreeSurface(swsurf);
1388 hwsurf = hwsurf2;
1389 swsurf = swsurf2;
1390 full = true;
1391 current_windowsize = windowsize;
1394 SDL_LockSurface(swsurf);
1395 if(full)
1396 redraw_borders(swsurf, screensize, windowsize);
1397 if(!console_mode) {
1398 draw_main_screen(swsurf, screensize);
1399 draw_status_area(swsurf, screensize);
1401 draw_messages(swsurf, screensize, windowsize);
1402 draw_command(swsurf, windowsize);
1404 //Draw modal dialog.
1405 if(state == WINSTATE_MODAL)
1406 try {
1407 draw_modal_dialog(swsurf, modmsg, modconfirm);
1408 } catch(...) {
1410 SDL_UnlockSurface(swsurf);
1411 SDL_BlitSurface(swsurf, NULL, hwsurf, NULL);
1412 SDL_UpdateRect(hwsurf, 0, 0, 0, 0);
1415 void poll_inputs_internal() throw(std::bad_alloc)
1417 SDL_Event e;
1418 while(state != WINSTATE_NORMAL) {
1419 if(SDL_WaitEvent(&e))
1420 do_event(e);
1421 if(delayed_close_flag) {
1422 state = WINSTATE_NORMAL;
1423 return;
1428 void window::poll_inputs() throw(std::bad_alloc)
1430 SDL_Event e;
1431 while(1) {
1432 assert(state == WINSTATE_NORMAL);
1433 if(!pause_active && !SDL_PollEvent(&e))
1434 break;
1435 else if(!pause_active)
1436 do_event(e);
1437 else if(SDL_WaitEvent(&e))
1438 do_event(e);
1442 std::map<std::string, std::string>& window::get_emustatus() throw()
1444 return emustatus;
1447 void window::paused(bool enable) throw()
1449 pause_active = enable;
1450 notify_screen_update();
1453 void window::sound_enable(bool enable) throw()
1455 sound_enabled = enable;
1456 SDL_PauseAudio(enable ? 0 : 1);
1459 namespace
1461 function_ptr_command<const std::string&> enable_sound("enable-sound", "Enable/Disable sound",
1462 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
1463 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
1464 std::string s = args;
1465 if(s == "on" || s == "true" || s == "1" || s == "enable" || s == "enabled")
1466 window::sound_enable(true);
1467 else if(s == "off" || s == "false" || s == "0" || s == "disable" || s == "disabled")
1468 window::sound_enable(false);
1469 else
1470 throw std::runtime_error("Bad sound setting");
1473 function_ptr_command<> identify_key("identify-key", "Identify a key",
1474 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1475 []() throw(std::bad_alloc, std::runtime_error) {
1476 identify();
1479 function_ptr_command<> scroll_up("scroll-up", "Scroll messages a page up",
1480 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1481 []() throw(std::bad_alloc, std::runtime_error) {
1482 if(messagebuffer_first_show > maxmessages)
1483 messagebuffer_first_show -= maxmessages;
1484 else
1485 messagebuffer_first_show = 0;
1486 if(messagebuffer_first_show < messagebuffer_first_seq)
1487 messagebuffer_first_show = messagebuffer_first_seq;
1488 window::notify_screen_update();
1491 function_ptr_command<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1492 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1493 []() throw(std::bad_alloc, std::runtime_error) {
1494 messagebuffer_first_show = messagebuffer_first_seq;
1495 window::notify_screen_update();
1498 function_ptr_command<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1499 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1500 []() throw(std::bad_alloc, std::runtime_error) {
1501 if(messagebuffer_next_seq < maxmessages)
1502 messagebuffer_first_show = 0;
1503 else
1504 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1505 window::notify_screen_update();
1508 function_ptr_command<> scrolldown("scroll-down", "Scroll messages a page down",
1509 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1510 []() throw(std::bad_alloc, std::runtime_error) {
1511 messagebuffer_first_show += maxmessages;
1512 if(messagebuffer_next_seq < maxmessages)
1513 messagebuffer_first_show = 0;
1514 else if(messagebuffer_next_seq < messagebuffer_first_show + maxmessages)
1515 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1516 window::notify_screen_update();
1519 function_ptr_command<> toggle_console("toggle-console", "Toggle console between small and full window",
1520 "Syntax: toggle-console\nToggles console between small and large.\n",
1521 []() throw(std::bad_alloc, std::runtime_error) {
1522 console_mode = !console_mode;
1523 if(console_mode)
1524 maxmessages = swsurf ? (swsurf->h - 38) / 16 : 36;
1525 else
1526 maxmessages = MAXMESSAGES;
1527 if(messagebuffer_next_seq < maxmessages)
1528 messagebuffer_first_show = 0;
1529 else
1530 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1531 window::notify_screen_update(true);
1535 void window::wait_usec(uint64_t usec) throw(std::bad_alloc)
1537 wait_canceled = false;
1538 uint64_t end_at = get_utime() + usec;
1539 while(!wait_canceled) {
1540 SDL_Event e;
1541 while(SDL_PollEvent(&e))
1542 do_event(e);
1543 uint64_t curtime = get_utime();
1544 if(curtime > end_at || wait_canceled)
1545 break;
1546 if(end_at > curtime + 10000)
1547 ::wait_usec(10000);
1548 else
1549 ::wait_usec(end_at - curtime);
1551 wait_canceled = false;
1554 void window::fatal_error() throw()
1556 try {
1557 message("PANIC: Cannot continue, press ESC or close window to exit.");
1558 //Force redraw.
1559 notify_screen_update(true);
1560 } catch(...) {
1561 //Just crash.
1562 exit(1);
1564 time_t curtime = time(NULL);
1565 struct tm* tm = localtime(&curtime);
1566 char buffer[1024];
1567 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1568 system_log << "-----------------------------------------------------------------------" << std::endl;
1569 system_log << "lsnes paniced at " << buffer << std::endl;
1570 system_log << "-----------------------------------------------------------------------" << std::endl;
1571 system_log.close();
1572 while(true) {
1573 SDL_Event e;
1574 if(SDL_WaitEvent(&e)) {
1575 if(e.type == SDL_QUIT)
1576 exit(1);
1577 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
1578 exit(1);
1583 void window::cancel_wait() throw()
1585 wait_canceled = true;
1588 void window::play_audio_sample(uint16_t left, uint16_t right) throw()
1590 sampledup_ctr += sampledup_inc;
1591 while(sampledup_ctr < sampledup_mod) {
1592 audiobuf[audiobuf_put++] = left;
1593 audiobuf[audiobuf_put++] = right;
1594 if(audiobuf_put == audiobuf_size)
1595 audiobuf_put = 0;
1596 sampledup_ctr += sampledup_inc;
1598 sampledup_ctr -= sampledup_mod;
1601 void window::set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_t hscl, uint32_t vscl)
1603 vc_xoffset = xoffset;
1604 vc_yoffset = yoffset;
1605 vc_hscl = hscl;
1606 vc_vscl = vscl;