Try to fix nasty corner cases of failing loads
[lsnes.git] / window-sdl.cpp
blob8d6ff758d9b42d3f07beb05cc37b6657dc97e74b
1 #include "window.hpp"
2 #include "render.hpp"
3 #include "command.hpp"
4 #include "misc.hpp"
5 #include "lsnes.hpp"
6 #include "settings.hpp"
7 #include <vector>
8 #include <iostream>
9 #include <csignal>
10 #include "keymapper.hpp"
11 #include "framerate.hpp"
12 #include <sstream>
13 #include <fstream>
14 #include <cassert>
16 #define WATCHDOG_TIMEOUT 15
17 #define MAXMESSAGES 6
18 #define MSGHISTORY 1000
19 #define MAXHISTORY 1000
20 #define JOYTHRESHOLD 3200
22 #include <SDL.h>
23 #include <string>
24 #include <map>
25 #include <stdexcept>
27 #define SDL_DEV_NONE 0
28 #define SDL_DEV_KEYBOARD 1
29 #define SDL_DEV_JOYAXIS 2
30 #define SDL_DEV_JOYBUTTON 3
31 #define SDL_DEV_JOYHAT 4
32 // Limit the emulator to ~30fps.
33 #define MIN_UPDATE_TIME 33
36 namespace
38 bool wait_canceled;
39 SDL_TimerID tid;
41 void sigalrm_handler(int s)
43 _exit(1);
46 Uint32 timer_cb(Uint32 interval, void* param)
48 SDL_Event e;
49 e.type = SDL_USEREVENT;
50 e.user.code = 0;
51 SDL_PushEvent(&e);
52 return interval;
55 struct sdl_modifier
57 const char* name;
58 const char* linkname;
59 unsigned sdlvalue;
60 } modifiers_table[] = {
61 { "ctrl", NULL, 0 },
62 { "lctrl", "ctrl", KMOD_LCTRL },
63 { "rctrl", "ctrl", KMOD_RCTRL },
64 { "alt", NULL, 0 },
65 { "lalt", "alt", KMOD_LALT },
66 { "ralt", "alt", KMOD_RALT },
67 { "shift", NULL, 0 },
68 { "lshift", "shift", KMOD_LSHIFT },
69 { "rshift", "shift", KMOD_RSHIFT },
70 { "meta", NULL, 0 },
71 { "lmeta", "meta", KMOD_LMETA },
72 { "rmeta", "meta", KMOD_RMETA },
73 { "num", NULL, KMOD_NUM },
74 { "caps", NULL, KMOD_CAPS },
75 { "mode", NULL, KMOD_MODE },
76 { NULL, NULL, 0 }
79 struct sdl_key
81 const char* name;
82 unsigned symbol;
83 } keys_table[] = {
84 {"backspace", SDLK_BACKSPACE },
85 {"tab", SDLK_TAB },
86 {"clear", SDLK_CLEAR },
87 {"return", SDLK_RETURN },
88 {"pause", SDLK_PAUSE },
89 {"escape", SDLK_ESCAPE },
90 {"space", SDLK_SPACE },
91 {"exclaim", SDLK_EXCLAIM },
92 {"quotedbl", SDLK_QUOTEDBL },
93 {"hash", SDLK_HASH },
94 {"dollar", SDLK_DOLLAR },
95 {"ampersand", SDLK_AMPERSAND },
96 {"quote", SDLK_QUOTE },
97 {"leftparen", SDLK_LEFTPAREN },
98 {"rightparen", SDLK_RIGHTPAREN },
99 {"asterisk", SDLK_ASTERISK },
100 {"plus", SDLK_PLUS },
101 {"comma", SDLK_COMMA },
102 {"minus", SDLK_MINUS },
103 {"period", SDLK_PERIOD },
104 {"slash", SDLK_SLASH },
105 {"0", SDLK_0 },
106 {"1", SDLK_1 },
107 {"2", SDLK_2 },
108 {"3", SDLK_3 },
109 {"4", SDLK_4 },
110 {"5", SDLK_5 },
111 {"6", SDLK_6 },
112 {"7", SDLK_7 },
113 {"8", SDLK_8 },
114 {"9", SDLK_9 },
115 {"colon", SDLK_COLON },
116 {"semicolon", SDLK_SEMICOLON },
117 {"less", SDLK_LESS },
118 {"equals", SDLK_EQUALS },
119 {"greater", SDLK_GREATER },
120 {"question", SDLK_QUESTION },
121 {"at", SDLK_AT },
122 {"leftbracket", SDLK_LEFTBRACKET },
123 {"backslash", SDLK_BACKSLASH },
124 {"rightbracket", SDLK_RIGHTBRACKET },
125 {"caret", SDLK_CARET },
126 {"underscore", SDLK_UNDERSCORE },
127 {"backquote", SDLK_BACKQUOTE },
128 {"a", SDLK_a },
129 {"b", SDLK_b },
130 {"c", SDLK_c },
131 {"d", SDLK_d },
132 {"e", SDLK_e },
133 {"f", SDLK_f },
134 {"g", SDLK_g },
135 {"h", SDLK_h },
136 {"i", SDLK_i },
137 {"j", SDLK_j },
138 {"k", SDLK_k },
139 {"l", SDLK_l },
140 {"m", SDLK_m },
141 {"n", SDLK_n },
142 {"o", SDLK_o },
143 {"p", SDLK_p },
144 {"q", SDLK_q },
145 {"r", SDLK_r },
146 {"s", SDLK_s },
147 {"t", SDLK_t },
148 {"u", SDLK_u },
149 {"v", SDLK_v },
150 {"w", SDLK_w },
151 {"x", SDLK_x },
152 {"y", SDLK_y },
153 {"z", SDLK_z },
154 {"delete", SDLK_DELETE },
155 {"world_0", SDLK_WORLD_0 },
156 {"world_1", SDLK_WORLD_1 },
157 {"world_2", SDLK_WORLD_2 },
158 {"world_3", SDLK_WORLD_3 },
159 {"world_4", SDLK_WORLD_4 },
160 {"world_5", SDLK_WORLD_5 },
161 {"world_6", SDLK_WORLD_6 },
162 {"world_7", SDLK_WORLD_7 },
163 {"world_8", SDLK_WORLD_8 },
164 {"world_9", SDLK_WORLD_9 },
165 {"world_10", SDLK_WORLD_10 },
166 {"world_11", SDLK_WORLD_11 },
167 {"world_12", SDLK_WORLD_12 },
168 {"world_13", SDLK_WORLD_13 },
169 {"world_14", SDLK_WORLD_14 },
170 {"world_15", SDLK_WORLD_15 },
171 {"world_16", SDLK_WORLD_16 },
172 {"world_17", SDLK_WORLD_17 },
173 {"world_18", SDLK_WORLD_18 },
174 {"world_19", SDLK_WORLD_19 },
175 {"world_20", SDLK_WORLD_20 },
176 {"world_21", SDLK_WORLD_21 },
177 {"world_22", SDLK_WORLD_22 },
178 {"world_23", SDLK_WORLD_23 },
179 {"world_24", SDLK_WORLD_24 },
180 {"world_25", SDLK_WORLD_25 },
181 {"world_26", SDLK_WORLD_26 },
182 {"world_27", SDLK_WORLD_27 },
183 {"world_28", SDLK_WORLD_28 },
184 {"world_29", SDLK_WORLD_29 },
185 {"world_30", SDLK_WORLD_30 },
186 {"world_31", SDLK_WORLD_31 },
187 {"world_32", SDLK_WORLD_32 },
188 {"world_33", SDLK_WORLD_33 },
189 {"world_34", SDLK_WORLD_34 },
190 {"world_35", SDLK_WORLD_35 },
191 {"world_36", SDLK_WORLD_36 },
192 {"world_37", SDLK_WORLD_37 },
193 {"world_38", SDLK_WORLD_38 },
194 {"world_39", SDLK_WORLD_39 },
195 {"world_40", SDLK_WORLD_40 },
196 {"world_41", SDLK_WORLD_41 },
197 {"world_42", SDLK_WORLD_42 },
198 {"world_43", SDLK_WORLD_43 },
199 {"world_44", SDLK_WORLD_44 },
200 {"world_45", SDLK_WORLD_45 },
201 {"world_46", SDLK_WORLD_46 },
202 {"world_47", SDLK_WORLD_47 },
203 {"world_48", SDLK_WORLD_48 },
204 {"world_49", SDLK_WORLD_49 },
205 {"world_50", SDLK_WORLD_50 },
206 {"world_51", SDLK_WORLD_51 },
207 {"world_52", SDLK_WORLD_52 },
208 {"world_53", SDLK_WORLD_53 },
209 {"world_54", SDLK_WORLD_54 },
210 {"world_55", SDLK_WORLD_55 },
211 {"world_56", SDLK_WORLD_56 },
212 {"world_57", SDLK_WORLD_57 },
213 {"world_58", SDLK_WORLD_58 },
214 {"world_59", SDLK_WORLD_59 },
215 {"world_60", SDLK_WORLD_60 },
216 {"world_61", SDLK_WORLD_61 },
217 {"world_62", SDLK_WORLD_62 },
218 {"world_63", SDLK_WORLD_63 },
219 {"world_64", SDLK_WORLD_64 },
220 {"world_65", SDLK_WORLD_65 },
221 {"world_66", SDLK_WORLD_66 },
222 {"world_67", SDLK_WORLD_67 },
223 {"world_68", SDLK_WORLD_68 },
224 {"world_69", SDLK_WORLD_69 },
225 {"world_70", SDLK_WORLD_70 },
226 {"world_71", SDLK_WORLD_71 },
227 {"world_72", SDLK_WORLD_72 },
228 {"world_73", SDLK_WORLD_73 },
229 {"world_74", SDLK_WORLD_74 },
230 {"world_75", SDLK_WORLD_75 },
231 {"world_76", SDLK_WORLD_76 },
232 {"world_77", SDLK_WORLD_77 },
233 {"world_78", SDLK_WORLD_78 },
234 {"world_79", SDLK_WORLD_79 },
235 {"world_80", SDLK_WORLD_80 },
236 {"world_81", SDLK_WORLD_81 },
237 {"world_82", SDLK_WORLD_82 },
238 {"world_83", SDLK_WORLD_83 },
239 {"world_84", SDLK_WORLD_84 },
240 {"world_85", SDLK_WORLD_85 },
241 {"world_86", SDLK_WORLD_86 },
242 {"world_87", SDLK_WORLD_87 },
243 {"world_88", SDLK_WORLD_88 },
244 {"world_89", SDLK_WORLD_89 },
245 {"world_90", SDLK_WORLD_90 },
246 {"world_91", SDLK_WORLD_91 },
247 {"world_92", SDLK_WORLD_92 },
248 {"world_93", SDLK_WORLD_93 },
249 {"world_94", SDLK_WORLD_94 },
250 {"world_95", SDLK_WORLD_95 },
251 {"kp0", SDLK_KP0 },
252 {"kp1", SDLK_KP1 },
253 {"kp2", SDLK_KP2 },
254 {"kp3", SDLK_KP3 },
255 {"kp4", SDLK_KP4 },
256 {"kp5", SDLK_KP5 },
257 {"kp6", SDLK_KP6 },
258 {"kp7", SDLK_KP7 },
259 {"kp8", SDLK_KP8 },
260 {"kp9", SDLK_KP9 },
261 {"kp_period", SDLK_KP_PERIOD },
262 {"kp_divide", SDLK_KP_DIVIDE },
263 {"kp_multiply", SDLK_KP_MULTIPLY },
264 {"kp_minus", SDLK_KP_MINUS },
265 {"kp_plus", SDLK_KP_PLUS },
266 {"kp_enter", SDLK_KP_ENTER },
267 {"kp_equals", SDLK_KP_EQUALS },
268 {"up", SDLK_UP },
269 {"down", SDLK_DOWN },
270 {"right", SDLK_RIGHT },
271 {"left", SDLK_LEFT },
272 {"insert", SDLK_INSERT },
273 {"home", SDLK_HOME },
274 {"end", SDLK_END },
275 {"pageup", SDLK_PAGEUP },
276 {"pagedown", SDLK_PAGEDOWN },
277 {"f1", SDLK_F1 },
278 {"f2", SDLK_F2 },
279 {"f3", SDLK_F3 },
280 {"f4", SDLK_F4 },
281 {"f5", SDLK_F5 },
282 {"f6", SDLK_F6 },
283 {"f7", SDLK_F7 },
284 {"f8", SDLK_F8 },
285 {"f9", SDLK_F9 },
286 {"f10", SDLK_F10 },
287 {"f11", SDLK_F11 },
288 {"f12", SDLK_F12 },
289 {"f13", SDLK_F13 },
290 {"f14", SDLK_F14 },
291 {"f15", SDLK_F15 },
292 {"numlock", SDLK_NUMLOCK },
293 {"capslock", SDLK_CAPSLOCK },
294 {"scrollock", SDLK_SCROLLOCK },
295 {"rshift", SDLK_RSHIFT },
296 {"lshift", SDLK_LSHIFT },
297 {"rctrl", SDLK_RCTRL },
298 {"lctrl", SDLK_LCTRL },
299 {"ralt", SDLK_RALT },
300 {"lalt", SDLK_LALT },
301 {"rmeta", SDLK_RMETA },
302 {"lmeta", SDLK_LMETA },
303 {"lsuper", SDLK_LSUPER },
304 {"rsuper", SDLK_RSUPER },
305 {"mode", SDLK_MODE },
306 {"compose", SDLK_COMPOSE },
307 {"help", SDLK_HELP },
308 {"print", SDLK_PRINT },
309 {"sysreq", SDLK_SYSREQ },
310 {"break", SDLK_BREAK },
311 {"menu", SDLK_MENU },
312 {"power", SDLK_POWER },
313 {"euro", SDLK_EURO },
314 {"undo", SDLK_UNDO },
315 {NULL, 0 }
318 std::map<unsigned, modifier*> supported_modifiers;
319 std::map<unsigned, keygroup*> scancodekeys;
320 std::map<unsigned, keygroup*> symbolkeys;
321 std::map<unsigned, keygroup*> joyaxis;
322 std::map<unsigned, keygroup*> joybutton;
323 std::map<unsigned, keygroup*> joyhat;
325 void init_keys()
327 struct sdl_modifier* m = modifiers_table;
328 while(m->name) {
329 modifier* m2;
330 if(m->linkname)
331 m2 = new modifier(m->name, m->linkname);
332 else
333 m2 = new modifier(m->name);
334 if(m->sdlvalue)
335 supported_modifiers[m->sdlvalue] = m2;
336 m++;
338 struct sdl_key* k = keys_table;
339 while(k->name) {
340 symbolkeys[k->symbol] = new keygroup(k->name, keygroup::KT_KEY);
341 k++;
343 for(unsigned i = 0; i < 256; i++) {
344 std::ostringstream x;
345 x << "key" << i;
346 scancodekeys[i] = new keygroup(x.str(), keygroup::KT_KEY);
350 void init_joysticks()
352 int joysticks = SDL_NumJoysticks();
353 if(!joysticks) {
354 window::out() << "No joysticks detected." << std::endl;
355 } else {
356 window::out() << joysticks << " joystick(s) detected." << std::endl;
357 for(int i = 0; i < joysticks; i++) {
358 SDL_Joystick* j = SDL_JoystickOpen(i);
359 if(!j) {
360 window::out() << "Joystick #" << i << ": Can't open!" << std::endl;
361 continue;
363 window::out() << "Joystick #" << i << ": " << SDL_JoystickName(i) << "("
364 << SDL_JoystickNumAxes(j) << " axes, " << SDL_JoystickNumButtons(j)
365 << " buttons, " << SDL_JoystickNumHats(j) << " hats)." << std::endl;
366 for(int k = 0; k < SDL_JoystickNumAxes(j); k++) {
367 unsigned num = 256 * i + k;
368 std::ostringstream x;
369 x << "joystick" << i << "axis" << k;
370 joyaxis[num] = new keygroup(x.str(), keygroup::KT_AXIS_PAIR);
372 for(int k = 0; k < SDL_JoystickNumButtons(j); k++) {
373 unsigned num = 256 * i + k;
374 std::ostringstream x;
375 x << "joystick" << i << "button" << k;
376 joybutton[num] = new keygroup(x.str(), keygroup::KT_KEY);
378 for(int k = 0; k < SDL_JoystickNumHats(j); k++) {
379 unsigned num = 256 * i + k;
380 std::ostringstream x;
381 x << "joystick" << i << "hat" << k;
382 joyhat[num] = new keygroup(x.str(), keygroup::KT_HAT);
388 struct identify_helper : public keygroup::key_listener
390 void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey,
391 bool polarity, const std::string& name)
393 if(!polarity)
394 _keys = _keys + "Name: " + name + "\n";
396 bool got_it()
398 return (_keys != "");
400 std::string keys()
402 return _keys;
404 std::string _keys;
407 struct key_eater : public keygroup::key_listener
409 void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey,
410 bool polarity, const std::string& name)
412 //Just eat it.
414 } keyeater;
416 void process_input_event(SDL_Event* e, bool identify)
418 identify_helper h;
419 if(identify)
420 keygroup::set_exclusive_key_listener(&h);
421 modifier_set modifiers;
422 if(e->type == SDL_KEYDOWN || e->type == SDL_KEYUP) {
423 SDL_keysym sym = e->key.keysym;
424 uint8_t scancode = sym.scancode;
425 unsigned symbol = sym.sym;
426 for(auto k = supported_modifiers.begin(); k != supported_modifiers.end(); ++k)
427 if(sym.mod & k->first)
428 modifiers.add(*k->second);
429 scancodekeys[scancode]->set_position((e->type == SDL_KEYDOWN) ? 1 : 0, modifiers);
430 if(symbolkeys.count(symbol))
431 symbolkeys[symbol]->set_position((e->type == SDL_KEYDOWN) ? 1 : 0, modifiers);
432 } else if(e->type == SDL_JOYAXISMOTION) {
433 unsigned num = static_cast<unsigned>(e->jaxis.which) * 256 +
434 static_cast<unsigned>(e->jaxis.axis);
435 if(joyaxis.count(num))
436 joyaxis[num]->set_position(e->jaxis.value, modifiers);
437 } else if(e->type == SDL_JOYHATMOTION) {
438 unsigned num = static_cast<unsigned>(e->jhat.which) * 256 +
439 static_cast<unsigned>(e->jhat.hat);
440 short v = 0;
441 if(e->jhat.value & SDL_HAT_UP)
442 v |= 1;
443 if(e->jhat.value & SDL_HAT_RIGHT)
444 v |= 2;
445 if(e->jhat.value & SDL_HAT_DOWN)
446 v |= 4;
447 if(e->jhat.value & SDL_HAT_LEFT)
448 v |= 8;
449 if(joyhat.count(num))
450 joyhat[num]->set_position(v, modifiers);
451 } else if(e->type == SDL_JOYBUTTONDOWN || e->type == SDL_JOYBUTTONUP) {
452 unsigned num = static_cast<unsigned>(e->jbutton.which) * 256 +
453 static_cast<unsigned>(e->jbutton.button);
454 if(joybutton.count(num))
455 joybutton[num]->set_position((e->type == SDL_JOYBUTTONDOWN), modifiers);
457 if(identify) {
458 if(h.got_it())
459 window::modal_message(h.keys(), false);
460 keygroup::set_exclusive_key_listener(NULL);
465 extern uint32_t fontdata[];
468 namespace
470 uint32_t mouse_mask = 0;
471 uint32_t vc_xoffset;
472 uint32_t vc_yoffset;
473 uint32_t vc_hscl = 1;
474 uint32_t vc_vscl = 1;
475 bool sdl_init;
476 bool modconfirm;
477 bool modal_return_flag;
478 bool delayed_close_flag;
479 std::string modmsg;
480 std::string command_buf;
481 bool command_overwrite;
482 size_t command_cursor;
483 unsigned old_screen_w;
484 unsigned old_screen_h;
485 unsigned state;
486 std::map<std::string, std::string> emustatus;
487 std::map<uint64_t, std::string> messagebuffer;
488 uint64_t messagebuffer_next_seq;
489 uint64_t messagebuffer_first_seq;
490 uint64_t messagebuffer_first_show;
491 bool console_mode;
492 uint32_t maxmessages;
493 std::list<std::string> commandhistory;
494 std::list<std::string>::iterator commandhistory_itr;
495 screen* current_screen;
496 SDL_Surface* hwsurf;
497 bool pause_active;
498 uint64_t last_ui_update;
499 bool screen_is_dirty;
500 std::ofstream system_log;
501 SDL_keysym autorepeating_key;
502 unsigned autorepeat_phase = 0;
503 unsigned autorepeat_timecounter = 0;
504 numeric_setting autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
505 numeric_setting autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
508 void poll_inputs_internal() throw(std::bad_alloc);
510 namespace
512 const size_t audiobuf_size = 8192;
513 uint16_t audiobuf[audiobuf_size];
514 volatile size_t audiobuf_get = 0;
515 volatile size_t audiobuf_put = 0;
516 uint64_t sampledup_ctr = 0;
517 uint64_t sampledup_inc = 0;
518 uint64_t sampledup_mod = 1;
519 Uint16 format = AUDIO_S16SYS;
520 bool stereo = true;
521 bool sound_enabled = true;
523 void calculate_sampledup(uint32_t real_rate)
525 sampledup_ctr = 0;
526 sampledup_inc = 64081;
527 sampledup_mod = 2 * real_rate + 64081;
530 void audiocb(void* dummy, Uint8* stream, int len)
532 static uint16_t lprev = 32768;
533 static uint16_t rprev = 32768;
534 if(!sound_enabled)
535 lprev = rprev = 32768;
536 uint16_t bias = (format == AUDIO_S8 || format == AUDIO_S16LSB || format == AUDIO_S16MSB || format ==
537 AUDIO_S16SYS) ? 32768 : 0;
538 while(len > 0) {
539 uint16_t l, r;
540 if(audiobuf_get == audiobuf_put) {
541 l = lprev;
542 r = rprev;
543 } else {
544 l = lprev = audiobuf[audiobuf_get++];
545 r = rprev = audiobuf[audiobuf_get++];
546 if(audiobuf_get == audiobuf_size)
547 audiobuf_get = 0;
549 if(!stereo)
550 l = l / 2 + r / 2;
551 if(format == AUDIO_U8 || format == AUDIO_S8) {
552 stream[0] = (l - bias) >> 8;
553 if(stereo)
554 stream[1] = (r - bias) >> 8;
555 stream += (stereo ? 2 : 1);
556 len -= (stereo ? 2 : 1);
557 } else if(format == AUDIO_S16SYS || format == AUDIO_U16SYS) {
558 reinterpret_cast<uint16_t*>(stream)[0] = (l - bias);
559 if(stereo)
560 reinterpret_cast<int16_t*>(stream)[1] = (r - bias);
561 stream += (stereo ? 4 : 2);
562 len -= (stereo ? 4 : 2);
563 } else if(format == AUDIO_S16LSB || format == AUDIO_U16LSB) {
564 stream[0] = (l - bias);
565 stream[1] = (l - bias) >> 8;
566 if(stereo) {
567 stream[2] = (r - bias);
568 stream[3] = (r - bias) >> 8;
570 stream += (stereo ? 4 : 2);
571 len -= (stereo ? 4 : 2);
572 } else if(format == AUDIO_S16MSB || format == AUDIO_U16MSB) {
573 stream[1] = (l - bias);
574 stream[0] = (l - bias) >> 8;
575 if(stereo) {
576 stream[3] = (r - bias);
577 stream[2] = (r - bias) >> 8;
579 stream += (stereo ? 4 : 2);
580 len -= (stereo ? 4 : 2);
585 void identify()
587 state = WINSTATE_IDENTIFY;
588 window::message("Press key to identify.");
589 window::notify_screen_update();
590 poll_inputs_internal();
593 std::string decode_string(std::string e)
595 std::string x;
596 for(size_t i = 0; i < e.length(); i += 4) {
597 char tmp[5] = {0};
598 uint32_t c1 = e[i] - 33;
599 uint32_t c2 = e[i + 1] - 33;
600 uint32_t c3 = e[i + 2] - 33;
601 uint32_t c4 = e[i + 3] - 33;
602 uint32_t c = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
603 if(c < 0x80) {
604 tmp[0] = c;
605 } else if(c < 0x800) {
606 tmp[0] = 0xC0 | (c >> 6);
607 tmp[1] = 0x80 | (c & 0x3F);
608 } else if(c < 0x10000) {
609 tmp[0] = 0xE0 | (c >> 12);
610 tmp[1] = 0x80 | ((c >> 6) & 0x3F);
611 tmp[2] = 0x80 | (c & 0x3F);
612 } else {
613 tmp[0] = 0xF0 | (c >> 18);
614 tmp[1] = 0x80 | ((c >> 12) & 0x3F);
615 tmp[2] = 0x80 | ((c >> 6) & 0x3F);
616 tmp[3] = 0x80 | (c & 0x3F);
618 x = x + tmp;
620 return x;
623 void draw_rectangle(uint8_t* data, uint32_t pitch, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
624 uint32_t color, uint32_t thickness)
626 for(uint32_t i = x1; i < x2; i++)
627 for(uint32_t j = 0; j < thickness; j++) {
628 reinterpret_cast<uint32_t*>(data + pitch * (y1 + j))[i] = color;
629 reinterpret_cast<uint32_t*>(data + pitch * (y2 - 1 - j))[i] = color;
631 for(uint32_t i = y1; i < y2; i++)
632 for(uint32_t j = 0; j < thickness; j++) {
633 reinterpret_cast<uint32_t*>(data + pitch * i)[x1 + j] = color;
634 reinterpret_cast<uint32_t*>(data + pitch * i)[x2 - 1 - j] = color;
638 std::vector<uint32_t> decode_utf8(std::string s)
640 std::vector<uint32_t> ret;
641 for(auto i = s.begin(); i != s.end(); i++) {
642 uint32_t j = static_cast<uint8_t>(*i);
643 if(j < 128)
644 ret.push_back(j);
645 else if(j < 192)
646 continue;
647 else if(j < 224) {
648 uint32_t j2 = static_cast<uint8_t>(*(++i));
649 ret.push_back((j - 192) * 64 + (j2 - 128));
650 } else if(j < 240) {
651 uint32_t j2 = static_cast<uint8_t>(*(++i));
652 uint32_t j3 = static_cast<uint8_t>(*(++i));
653 ret.push_back((j - 224) * 4096 + (j2 - 128) * 64 + (j3 - 128));
654 } else {
655 uint32_t j2 = static_cast<uint8_t>(*(++i));
656 uint32_t j3 = static_cast<uint8_t>(*(++i));
657 uint32_t j4 = static_cast<uint8_t>(*(++i));
658 ret.push_back((j - 240) * 262144 + (j2 - 128) * 4096 + (j3 - 128) * 64 + (j4 - 128));
661 return ret;
664 void draw_string(uint8_t* base, uint32_t pitch, std::vector<uint32_t> s, uint32_t x, uint32_t y,
665 uint32_t maxwidth, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
667 base += y * static_cast<size_t>(pitch) + 4 * x;
668 int32_t pos_x = 0;
669 int32_t pos_y = 0;
670 unsigned c = 0;
671 for(auto si = s.begin(); si != s.end(); si++) {
672 uint32_t old_x = pos_x;
673 uint32_t curstart = 16;
674 if(c == hilite_pos && hilite_mode == 1)
675 curstart = 14;
676 if(c == hilite_pos && hilite_mode == 2)
677 curstart = 0;
678 auto g = find_glyph(*si, pos_x, pos_y, 0, pos_x, pos_y);
679 if(pos_y)
680 pos_x = old_x;
681 if(g.second == 0) {
682 //Empty glyph.
683 for(unsigned j = 0; j < 16; j++) {
684 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
685 for(unsigned i = 0; i < g.first && old_x + i < maxwidth; i++)
686 ptr[old_x + i] = (j >= curstart) ? 0xFFFFFFFFU : 0;
688 } else {
689 //Narrow/Wide glyph.
690 for(unsigned j = 0; j < 16; j++) {
691 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
692 uint32_t dataword = fontdata[g.second + j / 4];
693 for(uint32_t i = 0; i < g.first && old_x + i < maxwidth; i++) {
694 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
695 1));
696 b ^= (j >= curstart);
697 ptr[old_x + i] = b ? 0xFFFFFFFFU : 0;
701 c++;
703 for(unsigned j = 0; j < 16; j++) {
704 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
705 uint32_t curstart = 16;
706 if(c == hilite_pos && hilite_mode == 1)
707 curstart = 14;
708 if(c == hilite_pos && hilite_mode == 2)
709 curstart = 0;
710 for(uint32_t i = pos_x; i < maxwidth; i++) {
711 ptr[i] = ((i - pos_x) < 8 && j >= curstart) ? 0xFFFFFFFFU : 0;
716 void draw_string(uint8_t* base, uint32_t pitch, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth,
717 uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
719 draw_string(base, pitch, decode_utf8(s), x, y, maxwidth, hilite_mode, hilite_pos);
722 void draw_command(uint8_t* base, uint32_t pitch, std::string s, size_t cursor, uint32_t x, uint32_t y,
723 uint32_t maxwidth, bool overwrite)
725 //FIXME, scroll text if too long.
726 uint32_t hilite_mode = overwrite ? 2 : 1;
727 auto s2 = decode_utf8(s);
728 draw_string(base, pitch, s2, x, y, maxwidth, hilite_mode, cursor);
731 void draw_modal_dialog(SDL_Surface* surf, std::string msg, bool confirm)
733 int32_t pos_x = 0;
734 int32_t pos_y = 0;
735 uint32_t width = 0;
736 uint32_t height = 0;
737 if(confirm)
738 msg = msg + "\n\nHit Enter to confirm, Esc to cancel";
739 else
740 msg = msg + "\n\nHit Enter or Esc to dismiss";
741 auto s2 = decode_utf8(msg);
742 for(auto i = s2.begin(); i != s2.end(); i++) {
743 auto g = find_glyph(*i, pos_x, pos_y, 0, pos_x, pos_y);
744 if(pos_x + g.first > width)
745 width = static_cast<uint32_t>(pos_x + g.first);
746 if(pos_y + 16 > static_cast<int32_t>(height))
747 height = static_cast<uint32_t>(pos_y + 16);
749 uint32_t x1;
750 uint32_t x2;
751 uint32_t y1;
752 uint32_t y2;
753 if(width + 12 >= static_cast<uint32_t>(surf->w)) {
754 x1 = 6;
755 x2 = surf->w - 6;
756 width = x2 - x1;
757 } else {
758 x1 = (surf->w - width) / 2;
759 x2 = x1 + width;
761 if(height + 12 >= static_cast<uint32_t>(surf->h)) {
762 y1 = 6;
763 y2 = surf->h - 6;
764 height = y2 - y1;
765 } else {
766 y1 = (surf->h - height) / 2;
767 y2 = y1 + height;
769 SDL_LockSurface(surf);
770 for(uint32_t j = y1 - 6; j < y2 + 6; j++)
771 memset(reinterpret_cast<uint8_t*>(surf->pixels) + j * surf->pitch + 4 * (x1 - 6), 0,
772 4 * (x2 - x1 + 12));
773 uint32_t bordercolor = (128 << surf->format->Gshift) | (255 << surf->format->Rshift);
774 draw_rectangle(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, x1 - 4, y1 - 4, x2 + 4, y2 + 4,
775 bordercolor, 2);
777 pos_x = 0;
778 pos_y = 0;
779 for(auto i = s2.begin(); i != s2.end(); i++) {
780 uint32_t ox = pos_x;
781 uint32_t oy = pos_y;
782 auto g = find_glyph(*i, pos_x, pos_y, 0, pos_x, pos_y);
783 if(static_cast<uint32_t>(pos_y) > height)
784 break;
785 uint8_t* base = reinterpret_cast<uint8_t*>(surf->pixels) + (y1 + oy) * surf->pitch +
786 4 * (x1 + ox);
787 if(g.second) {
788 //Narrow/Wide glyph.
789 for(unsigned j = 0; j < 16; j++) {
790 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + surf->pitch * j);
791 uint32_t dataword = fontdata[g.second + j / 4];
792 for(uint32_t i = 0; i < g.first && (ox + i) < width; i++) {
793 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
794 1));
795 ptr[i] = b ? bordercolor : 0;
803 void do_keyboard_command_edit(SDL_keysym k)
805 //These are not command edit!
806 if(k.sym == SDLK_ESCAPE)
807 return;
808 if(k.sym == SDLK_RETURN)
809 return;
810 if(k.sym == SDLK_KP_ENTER)
811 return;
812 //Map keys a bit if numlock is off.
813 if((k.mod & KMOD_NUM) == 0) {
814 switch(k.sym) {
815 case SDLK_KP0: k.sym = SDLK_INSERT; break;
816 case SDLK_KP1: k.sym = SDLK_END; break;
817 case SDLK_KP2: k.sym = SDLK_DOWN; break;
818 case SDLK_KP3: k.sym = SDLK_PAGEDOWN; break;
819 case SDLK_KP4: k.sym = SDLK_LEFT; break;
820 case SDLK_KP5: return;
821 case SDLK_KP6: k.sym = SDLK_RIGHT; break;
822 case SDLK_KP7: k.sym = SDLK_HOME; break;
823 case SDLK_KP8: k.sym = SDLK_UP; break;
824 case SDLK_KP9: k.sym = SDLK_PAGEUP; break;
825 case SDLK_KP_PERIOD: k.sym = SDLK_DELETE; break;
826 default:
827 break;
830 //Special editing operations.
831 switch(k.sym) {
832 case SDLK_INSERT:
833 command_overwrite = !command_overwrite;
834 window::notify_screen_update();
835 return;
836 case SDLK_END:
837 command_cursor = command_buf.length();
838 window::notify_screen_update();
839 return;
840 case SDLK_DOWN:
841 case SDLK_PAGEDOWN:
842 if(commandhistory_itr != commandhistory.begin()) {
843 commandhistory_itr--;
844 command_buf = *commandhistory_itr;
845 if(command_cursor > command_buf.length())
846 command_cursor = command_buf.length();
848 window::notify_screen_update();
849 return;
850 case SDLK_LEFT:
851 command_cursor = (command_cursor > 0) ? (command_cursor - 4) : 0;
852 window::notify_screen_update();
853 return;
854 case SDLK_RIGHT:
855 command_cursor = (command_cursor < command_buf.length()) ? (command_cursor + 4) :
856 command_buf.length();
857 window::notify_screen_update();
858 return;
859 case SDLK_HOME:
860 command_cursor = 0;
861 window::notify_screen_update();
862 return;
863 case SDLK_UP:
864 case SDLK_PAGEUP: {
865 auto tmp = commandhistory_itr;
866 if(++tmp != commandhistory.end()) {
867 commandhistory_itr++;
868 command_buf = *commandhistory_itr;
869 if(command_cursor > command_buf.length())
870 command_cursor = command_buf.length();
872 window::notify_screen_update();
873 return;
875 case SDLK_DELETE:
876 if(command_cursor < command_buf.length())
877 command_buf = command_buf.substr(0, command_cursor) +
878 command_buf.substr(command_cursor + 4);
879 window::notify_screen_update();
880 *commandhistory_itr = command_buf;
881 return;
882 case SDLK_BACKSPACE:
883 if(command_cursor > 0) {
884 command_buf = command_buf.substr(0, command_cursor - 4) +
885 command_buf.substr(command_cursor);
886 command_cursor -= 4;
888 window::notify_screen_update();
889 *commandhistory_itr = command_buf;
890 return;
891 default:
892 break;
895 //Not a special editing operation, insert/overwrite a character.
896 uint32_t code = k.unicode;
897 if(!code)
898 return;
899 uint8_t c1 = 33 + ((code >> 18) & 0x3F);
900 uint8_t c2 = 33 + ((code >> 12) & 0x3F);
901 uint8_t c3 = 33 + ((code >> 6) & 0x3F);
902 uint8_t c4 = 33 + (code & 0x3F);
903 if(command_overwrite && command_cursor < command_buf.length()) {
904 command_buf[command_cursor] = c1;
905 command_buf[command_cursor + 1] = c2;
906 command_buf[command_cursor + 2] = c3;
907 command_buf[command_cursor + 3] = c4;
908 command_cursor += 4;
909 } else {
910 std::string foo = " ";
911 foo[0] = c1;
912 foo[1] = c2;
913 foo[2] = c3;
914 foo[3] = c4;
915 command_buf = command_buf.substr(0, command_cursor) + foo + command_buf.substr(command_cursor);
916 command_cursor += 4;
918 *commandhistory_itr = command_buf;
919 window::notify_screen_update();
922 void do_event(SDL_Event& e) throw(std::bad_alloc)
924 alarm(WATCHDOG_TIMEOUT);
925 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE && e.key.keysym.mod == (KMOD_LCTRL |
926 KMOD_LALT))
927 exit(1);
928 if(e.type == SDL_USEREVENT && e.user.code == 0) {
929 if(screen_is_dirty)
930 window::notify_screen_update();
932 SDLKey key;
933 get_ticks_msec();
934 if(e.type == SDL_ACTIVEEVENT && e.active.gain && e.active.state == SDL_APPACTIVE) {
935 window::notify_screen_update();
936 return;
938 if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
939 key = e.key.keysym.sym;
941 if(e.type == SDL_QUIT && state == WINSTATE_IDENTIFY)
942 return;
943 if(e.type == SDL_QUIT && state == WINSTATE_MODAL) {
944 delayed_close_flag = true;
945 return;
947 if(e.type == SDL_QUIT) {
948 command::invokeC("quit-emulator");
949 state = WINSTATE_NORMAL;
950 return;
953 switch(state) {
954 case WINSTATE_NORMAL:
955 if(e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
956 int32_t xc = e.button.x;
957 int32_t yc = e.button.y;
958 xc = (xc - 6 - vc_xoffset) / vc_hscl;
959 yc = (yc - 6 - vc_yoffset) / vc_vscl;
960 if(e.button.button == SDL_BUTTON_LEFT) {
961 if(e.button.state == SDL_PRESSED)
962 mouse_mask |= 1;
963 else
964 mouse_mask &= ~1;
966 if(e.button.button == SDL_BUTTON_MIDDLE) {
967 if(e.button.state == SDL_PRESSED)
968 mouse_mask |= 2;
969 else
970 mouse_mask &= ~2;
972 if(e.button.button == SDL_BUTTON_RIGHT) {
973 if(e.button.state == SDL_PRESSED)
974 mouse_mask |= 4;
975 else
976 mouse_mask &= ~4;
979 std::ostringstream x;
980 x << "mouse_button " << xc << " " << yc << " " << mouse_mask;
981 command::invokeC(x.str());
984 if(e.type == SDL_KEYDOWN && key == SDLK_ESCAPE)
985 return;
986 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
987 state = WINSTATE_COMMAND;
988 command_buf = "";
989 command_cursor = 0;
990 commandhistory.push_front("");
991 if(commandhistory.size() > MAXHISTORY)
992 commandhistory.pop_back();
993 commandhistory_itr = commandhistory.begin();
994 window::notify_screen_update();
995 poll_inputs_internal();
996 return;
998 process_input_event(&e, false);
999 break;
1000 case WINSTATE_MODAL:
1001 //Send the key and eat it (prevent input from getting confused).
1002 keygroup::set_exclusive_key_listener(&keyeater);
1003 process_input_event(&e, false),
1004 keygroup::set_exclusive_key_listener(NULL);
1005 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
1006 state = WINSTATE_NORMAL;
1007 modconfirm = false;
1008 modal_return_flag = true;
1009 modmsg = "";
1010 window::notify_screen_update(true);
1011 return;
1013 if(e.type == SDL_KEYUP && (key == SDLK_RETURN || key == SDLK_KP_ENTER)) {
1014 state = WINSTATE_NORMAL;
1015 modal_return_flag = true;
1016 modmsg = "";
1017 window::notify_screen_update(true);
1018 return;
1020 break;
1021 case WINSTATE_COMMAND:
1022 //Send the key and eat it (prevent input from getting confused).
1023 keygroup::set_exclusive_key_listener(&keyeater);
1024 process_input_event(&e, false),
1025 keygroup::set_exclusive_key_listener(NULL);
1026 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) {
1027 state = WINSTATE_NORMAL;
1028 command_buf = "";
1029 window::notify_screen_update();
1030 if(commandhistory.front() == "")
1031 commandhistory.pop_front();
1032 return;
1034 if(e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_RETURN ||
1035 e.key.keysym.sym == SDLK_KP_ENTER)) {
1036 state = WINSTATE_NORMAL;
1037 if(commandhistory.front() == "")
1038 commandhistory.pop_front();
1039 command::invokeC(decode_string(command_buf));
1040 command_buf = "";
1041 window::notify_screen_update();
1042 autorepeat_phase = 0;
1043 return;
1045 if(e.type == SDL_KEYDOWN) {
1046 autorepeating_key = e.key.keysym;
1047 autorepeat_phase = 1;
1048 autorepeat_timecounter = 0;
1049 do_keyboard_command_edit(e.key.keysym);
1050 } else if(e.type == SDL_KEYUP) {
1051 autorepeat_phase = 0;
1053 if(e.type == SDL_USEREVENT && e.user.code == 0) {
1054 autorepeat_timecounter++;
1055 if(!autorepeat_phase)
1056 break;
1057 unsigned timeout = (autorepeat_phase == 1) ? autorepeat_first : autorepeat_subsequent;
1058 if(autorepeat_timecounter >= timeout) {
1059 do_keyboard_command_edit(autorepeating_key);
1060 autorepeat_timecounter = 0;
1061 autorepeat_phase = 2;
1064 break;
1065 case WINSTATE_IDENTIFY:
1066 process_input_event(&e, true);
1067 break;
1072 void window::init()
1074 signal(SIGALRM, sigalrm_handler);
1075 alarm(WATCHDOG_TIMEOUT);
1076 init_keys();
1077 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
1078 time_t curtime = time(NULL);
1079 struct tm* tm = localtime(&curtime);
1080 char buffer[1024];
1081 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1082 system_log << "-----------------------------------------------------------------------" << std::endl;
1083 system_log << "lsnes started at " << buffer << std::endl;
1084 system_log << "-----------------------------------------------------------------------" << std::endl;
1085 if(!sdl_init) {
1086 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
1087 SDL_EnableUNICODE(true);
1088 sdl_init = true;
1089 tid = SDL_AddTimer(MIN_UPDATE_TIME, timer_cb, NULL);
1091 state = WINSTATE_NORMAL;
1092 current_screen = NULL;
1093 pause_active = false;
1094 hwsurf = NULL;
1095 command_overwrite = false;
1096 old_screen_h = 0;
1097 old_screen_w = 0;
1098 modal_return_flag = false;
1099 delayed_close_flag = false;
1100 messagebuffer_next_seq = 0;
1101 messagebuffer_first_seq = 0;
1102 messagebuffer_first_show = 0;
1103 console_mode = false;
1104 maxmessages = MAXMESSAGES;
1106 notify_screen_update();
1107 std::string windowname = "lsnes-" + lsnes_version + "[" + bsnes_core_version + "]";
1108 SDL_WM_SetCaption(windowname.c_str(), "lsnes");
1110 init_joysticks();
1112 SDL_AudioSpec* desired = new SDL_AudioSpec();
1113 SDL_AudioSpec* obtained = new SDL_AudioSpec();
1115 desired->freq = 44100;
1116 desired->format = AUDIO_S16SYS;
1117 desired->channels = 2;
1118 desired->samples = 8192;
1119 desired->callback = audiocb;
1120 desired->userdata = NULL;
1122 if(SDL_OpenAudio(desired, obtained) < 0) {
1123 message("Audio can't be initialized, audio playback disabled");
1124 return;
1127 //Fill the parameters.
1128 calculate_sampledup(obtained->freq);
1129 format = obtained->format;
1130 stereo = (obtained->channels == 2);
1131 //GO!!!
1132 SDL_PauseAudio(0);
1135 void window::quit()
1137 time_t curtime = time(NULL);
1138 struct tm* tm = localtime(&curtime);
1139 char buffer[1024];
1140 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1141 system_log << "-----------------------------------------------------------------------" << std::endl;
1142 system_log << "lsnes shutting down at " << buffer << std::endl;
1143 system_log << "-----------------------------------------------------------------------" << std::endl;
1144 system_log.close();
1145 if(sdl_init) {
1146 SDL_RemoveTimer(tid);
1147 SDL_Quit();
1148 sdl_init = 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 command::invokeC("quit-emulator");
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 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2);
1176 system_log << forlog << std::endl;
1177 break;
1178 } else {
1179 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2.substr(0, s));
1180 system_log << forlog << std::endl;
1181 msg2 = msg2.substr(s + 1);
1185 if(locked_mode && messagebuffer_first_show + maxmessages < messagebuffer_next_seq)
1186 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1188 while(messagebuffer.size() > MSGHISTORY) {
1189 messagebuffer.erase(messagebuffer_first_seq++);
1190 if(messagebuffer_first_show < messagebuffer_first_seq)
1191 messagebuffer_first_show = messagebuffer_first_seq;
1193 notify_screen_update();
1196 void window::set_main_surface(screen& scr) throw()
1198 current_screen = &scr;
1199 notify_screen_update(true);
1202 void window::notify_screen_update(bool full) throw()
1204 uint64_t curtime = get_ticks_msec();
1205 if(!full && last_ui_update < curtime && last_ui_update + MIN_UPDATE_TIME > curtime) {
1206 screen_is_dirty = true;
1207 return;
1209 last_ui_update = curtime;
1210 screen_is_dirty = false;
1212 try {
1213 std::ostringstream y;
1214 y << get_framerate();
1215 emustatus["FPS"] = y.str();
1216 } catch(...) {
1219 std::string command_showas = decode_string(command_buf);
1220 uint32_t screen_w = 512;
1221 uint32_t screen_h = 448;
1222 if(current_screen && current_screen->width >= 512 && current_screen->height >= 448) {
1223 screen_w = current_screen->width;
1224 screen_h = current_screen->height;
1226 uint32_t win_w = ((screen_w < 512) ? 512 : ((screen_w + 15) / 16 * 16)) + 278;
1227 uint32_t win_h = screen_h + MAXMESSAGES * 16 + 48;
1228 if(!hwsurf || static_cast<uint32_t>(hwsurf->w) != win_w || static_cast<uint32_t>(hwsurf->h) != win_h ||
1229 old_screen_w != screen_w || old_screen_h != screen_h || full) {
1230 //Create/Resize the window.
1231 if(!hwsurf || static_cast<uint32_t>(hwsurf->w) != win_w || static_cast<uint32_t>(hwsurf->h) != win_h) {
1232 SDL_Surface* hwsurf2 = SDL_SetVideoMode(win_w, win_h, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
1233 if(!hwsurf2) {
1234 //We are in too fucked up state to even print error as message.
1235 std::cout << "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl;
1236 exit(1);
1238 hwsurf = hwsurf2;
1240 if(current_screen)
1241 current_screen->set_palette(hwsurf->format->Rshift, hwsurf->format->Gshift,
1242 hwsurf->format->Bshift);
1243 //Blank the screen and draw borders.
1244 SDL_LockSurface(hwsurf);
1245 memset(hwsurf->pixels, 0, win_h * hwsurf->pitch);
1246 uint32_t bordercolor = 255 << hwsurf->format->Gshift;
1247 if(console_mode) {
1248 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, 2, win_w - 2,
1249 win_h - 28, bordercolor, 2);
1250 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, win_h - 26,
1251 win_w - 2, win_h - 2, bordercolor, 2);
1252 } else {
1253 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, 2, screen_w + 10,
1254 screen_h + 10, bordercolor, 2);
1255 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, screen_w + 12, 2,
1256 screen_w + 276, screen_h + 10, bordercolor, 2);
1257 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, screen_h + 12,
1258 win_w - 2, screen_h + MAXMESSAGES * 16 + 20, bordercolor, 2);
1259 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2,
1260 screen_h + MAXMESSAGES * 16 + 22, win_w - 2, screen_h + MAXMESSAGES * 16 + 46,
1261 bordercolor, 2);
1263 SDL_UnlockSurface(hwsurf);
1264 old_screen_w = screen_w;
1265 old_screen_h = screen_h;
1267 SDL_LockSurface(hwsurf);
1268 if(!console_mode) {
1269 if(current_screen) {
1270 //Draw main screen (blanking background if needed).
1271 if(screen_w < current_screen->width || screen_h < current_screen->height)
1272 for(uint32_t i = 6; i < screen_h + 6; i++)
1273 memset(reinterpret_cast<uint8_t*>(hwsurf->pixels) + i * hwsurf->pitch + 24, 0,
1274 4 * screen_w);
1275 for(uint32_t i = 0; i < current_screen->height; i++)
1276 memcpy(reinterpret_cast<uint8_t*>(hwsurf->pixels) + (i + 6) * hwsurf->pitch + 24,
1277 reinterpret_cast<uint8_t*>(current_screen->memory) + current_screen->pitch * i,
1278 4 * current_screen->width);
1279 } else {
1280 //Draw blank.
1281 for(uint32_t i = 6; i < screen_h + 6; i++)
1282 memset(reinterpret_cast<uint8_t*>(hwsurf->pixels) + i * hwsurf->pitch + 24, 0,
1283 4 * screen_w);
1285 //Draw status.
1286 uint32_t status_x = screen_w + 16;
1287 uint32_t status_y = 6;
1288 for(auto i = emustatus.begin(); i != emustatus.end(); i++) {
1289 std::string msg = i->first + " " + i->second;
1290 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, msg, status_x, status_y,
1291 256);
1292 status_y += 16;
1294 while(status_y - 6 < screen_h / 16 * 16) {
1295 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "", status_x, status_y,
1296 256);
1297 status_y += 16;
1300 //Draw messages.
1301 uint32_t message_y;
1302 if(!console_mode)
1303 message_y = screen_h + 16;
1304 else
1305 message_y = 6;
1306 for(size_t j = 0; j < maxmessages; j++)
1307 try {
1308 std::ostringstream o;
1309 if(messagebuffer_first_show + j < messagebuffer_next_seq)
1310 o << (messagebuffer_first_show + j + 1) << ": "
1311 << messagebuffer[messagebuffer_first_show + j];
1312 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, o.str(), 6,
1313 message_y + 16 * j, win_w - 12);
1314 } catch(...) {
1316 if(messagebuffer_next_seq - messagebuffer_first_show > maxmessages)
1317 try {
1318 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "--More--", win_w - 76,
1319 message_y + 16 * maxmessages - 16, 64);
1320 } catch(...) {
1323 //Draw command_buf.
1324 uint32_t command_y = win_h - 22;
1325 try {
1326 if(state == WINSTATE_COMMAND)
1327 draw_command(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, command_showas,
1328 command_cursor / 4, 6, command_y, win_w - 12, command_overwrite);
1329 else
1330 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "", 6, command_y,
1331 win_w - 12);
1332 } catch(...) {
1334 //Draw modal dialog.
1335 if(state == WINSTATE_MODAL)
1336 try {
1337 draw_modal_dialog(hwsurf, modmsg, modconfirm);
1338 } catch(...) {
1340 SDL_UnlockSurface(hwsurf);
1341 SDL_Flip(hwsurf);
1344 void poll_inputs_internal() throw(std::bad_alloc)
1346 SDL_Event e;
1347 while(state != WINSTATE_NORMAL) {
1348 if(SDL_WaitEvent(&e))
1349 do_event(e);
1353 void window::poll_inputs() throw(std::bad_alloc)
1355 SDL_Event e;
1356 while(1) {
1357 assert(state == WINSTATE_NORMAL);
1358 if(!pause_active && !SDL_PollEvent(&e))
1359 break;
1360 else if(!pause_active)
1361 do_event(e);
1362 else if(SDL_WaitEvent(&e))
1363 do_event(e);
1367 std::map<std::string, std::string>& window::get_emustatus() throw()
1369 return emustatus;
1372 void window::paused(bool enable) throw()
1374 pause_active = enable;
1375 notify_screen_update();
1378 void window::sound_enable(bool enable) throw()
1380 sound_enabled = enable;
1381 SDL_PauseAudio(enable ? 0 : 1);
1384 namespace
1386 function_ptr_command<const std::string&> enable_sound("enable-sound", "Enable/Disable sound",
1387 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
1388 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
1389 std::string s = args;
1390 if(s == "on" || s == "true" || s == "1" || s == "enable" || s == "enabled")
1391 window::sound_enable(true);
1392 else if(s == "off" || s == "false" || s == "0" || s == "disable" || s == "disabled")
1393 window::sound_enable(false);
1394 else
1395 throw std::runtime_error("Bad sound setting");
1398 function_ptr_command<> identify_key("identify-key", "Identify a key",
1399 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1400 []() throw(std::bad_alloc, std::runtime_error) {
1401 identify();
1404 function_ptr_command<> scroll_up("scroll-up", "Scroll messages a page up",
1405 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1406 []() throw(std::bad_alloc, std::runtime_error) {
1407 if(messagebuffer_first_show > maxmessages)
1408 messagebuffer_first_show -= maxmessages;
1409 else
1410 messagebuffer_first_show = 0;
1411 if(messagebuffer_first_show < messagebuffer_first_seq)
1412 messagebuffer_first_show = messagebuffer_first_seq;
1413 window::notify_screen_update();
1416 function_ptr_command<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1417 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1418 []() throw(std::bad_alloc, std::runtime_error) {
1419 messagebuffer_first_show = messagebuffer_first_seq;
1420 window::notify_screen_update();
1423 function_ptr_command<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1424 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1425 []() throw(std::bad_alloc, std::runtime_error) {
1426 if(messagebuffer_next_seq < maxmessages)
1427 messagebuffer_first_show = 0;
1428 else
1429 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1430 window::notify_screen_update();
1433 function_ptr_command<> scrolldown("scroll-down", "Scroll messages a page down",
1434 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1435 []() throw(std::bad_alloc, std::runtime_error) {
1436 messagebuffer_first_show += maxmessages;
1437 if(messagebuffer_next_seq < maxmessages)
1438 messagebuffer_first_show = 0;
1439 else if(messagebuffer_next_seq < messagebuffer_first_show + maxmessages)
1440 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1441 window::notify_screen_update();
1444 function_ptr_command<> toggle_console("toggle-console", "Toggle console between small and full window",
1445 "Syntax: toggle-console\nToggles console between small and large.\n",
1446 []() throw(std::bad_alloc, std::runtime_error) {
1447 console_mode = !console_mode;
1448 if(console_mode)
1449 maxmessages = hwsurf ? (hwsurf->h - 38) / 16 : 36;
1450 else
1451 maxmessages = MAXMESSAGES;
1452 if(messagebuffer_next_seq < maxmessages)
1453 messagebuffer_first_show = 0;
1454 else
1455 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1456 window::notify_screen_update(true);
1459 function_ptr_command<tokensplitter&> joystickmode("axismode", "Set joystick axis mode",
1460 "Syntax: axismode joystick<num>axis<axis> <mode>\nSet joystick axis mode.\n",
1461 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
1462 std::string axis = t;
1463 std::string mode = t;
1464 unsigned i = 0;
1465 if(mode == "" || t)
1466 throw std::runtime_error("Expected exactly 2 parameters");
1467 keygroup* tomod = NULL;
1468 for(auto i = joyaxis.begin(); i != joyaxis.end(); ++i)
1469 if(i->second->name() == axis)
1470 tomod = i->second;
1471 if(!tomod)
1472 throw std::runtime_error("Invalid axis");
1473 if(mode == "axis")
1474 tomod->change_type(keygroup::KT_AXIS_PAIR);
1475 else if(mode == "axis_inverse")
1476 tomod->change_type(keygroup::KT_AXIS_PAIR_INVERSE);
1477 else if(mode == "pressure_0m")
1478 tomod->change_type(keygroup::KT_PRESSURE_0M);
1479 else if(mode == "pressure_0p")
1480 tomod->change_type(keygroup::KT_PRESSURE_0P);
1481 else if(mode == "pressure_m0")
1482 tomod->change_type(keygroup::KT_PRESSURE_M0);
1483 else if(mode == "pressure_mp")
1484 tomod->change_type(keygroup::KT_PRESSURE_MP);
1485 else if(mode == "pressure_p0")
1486 tomod->change_type(keygroup::KT_PRESSURE_P0);
1487 else if(mode == "pressure_pm")
1488 tomod->change_type(keygroup::KT_PRESSURE_PM);
1489 else if(mode == "disabled")
1490 tomod->change_type(keygroup::KT_DISABLED);
1491 else
1492 throw std::runtime_error("Bad axis mode");
1497 void window::wait_msec(uint64_t msec) throw(std::bad_alloc)
1499 wait_canceled = false;
1500 uint64_t basetime = get_ticks_msec();
1501 while(!wait_canceled) {
1502 if(msec > 10)
1503 SDL_Delay(10);
1504 else
1505 SDL_Delay(msec);
1506 SDL_Event e;
1507 while(SDL_PollEvent(&e))
1508 do_event(e);
1509 uint64_t passed = get_ticks_msec() - basetime;
1510 if(passed > msec)
1511 break;
1515 void window::fatal_error() throw()
1517 try {
1518 message("PANIC: Cannot continue, press ESC or close window to exit.");
1519 //Force redraw.
1520 notify_screen_update(true);
1521 } catch(...) {
1522 //Just crash.
1523 exit(1);
1525 time_t curtime = time(NULL);
1526 struct tm* tm = localtime(&curtime);
1527 char buffer[1024];
1528 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1529 system_log << "-----------------------------------------------------------------------" << std::endl;
1530 system_log << "lsnes paniced at " << buffer << std::endl;
1531 system_log << "-----------------------------------------------------------------------" << std::endl;
1532 system_log.close();
1533 while(true) {
1534 SDL_Event e;
1535 if(SDL_WaitEvent(&e)) {
1536 if(e.type == SDL_QUIT)
1537 exit(1);
1538 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
1539 exit(1);
1544 uint64_t get_ticks_msec() throw()
1546 static uint64_t tickbase = 0;
1547 static Uint32 last_ticks = 0;
1548 Uint32 cur_ticks = SDL_GetTicks();
1549 if(last_ticks > cur_ticks)
1550 tickbase += 0x100000000ULL;
1551 last_ticks = cur_ticks;
1552 return tickbase + cur_ticks;
1555 void window::cancel_wait() throw()
1557 wait_canceled = true;
1560 void window::play_audio_sample(uint16_t left, uint16_t right) throw()
1562 sampledup_ctr += sampledup_inc;
1563 while(sampledup_ctr < sampledup_mod) {
1564 audiobuf[audiobuf_put++] = left;
1565 audiobuf[audiobuf_put++] = right;
1566 if(audiobuf_put == audiobuf_size)
1567 audiobuf_put = 0;
1568 sampledup_ctr += sampledup_inc;
1570 sampledup_ctr -= sampledup_mod;
1573 void window::set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_t hscl, uint32_t vscl)
1575 vc_xoffset = xoffset;
1576 vc_yoffset = yoffset;
1577 vc_hscl = hscl;
1578 vc_vscl = vscl;