Do color upconversion when copying lcscreen -> screen
[lsnes.git] / SDL / window-sdl.cpp
blob49c0cc4b5449fc6b8a77ea06ffdd7b0f5b9cfdba
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
30 extern uint64_t in_paint_time;
32 namespace
34 bool wait_canceled;
35 SDL_TimerID tid;
37 void sigalrm_handler(int s)
39 _exit(1);
42 Uint32 timer_cb(Uint32 interval, void* param)
44 SDL_Event e;
45 e.type = SDL_USEREVENT;
46 e.user.code = 0;
47 SDL_PushEvent(&e);
48 return interval;
51 struct sdl_modifier
53 const char* name;
54 const char* linkname;
55 unsigned sdlvalue;
56 } modifiers_table[] = {
57 { "ctrl", NULL, 0 },
58 { "lctrl", "ctrl", KMOD_LCTRL },
59 { "rctrl", "ctrl", KMOD_RCTRL },
60 { "alt", NULL, 0 },
61 { "lalt", "alt", KMOD_LALT },
62 { "ralt", "alt", KMOD_RALT },
63 { "shift", NULL, 0 },
64 { "lshift", "shift", KMOD_LSHIFT },
65 { "rshift", "shift", KMOD_RSHIFT },
66 { "meta", NULL, 0 },
67 { "lmeta", "meta", KMOD_LMETA },
68 { "rmeta", "meta", KMOD_RMETA },
69 { "num", NULL, KMOD_NUM },
70 { "caps", NULL, KMOD_CAPS },
71 { "mode", NULL, KMOD_MODE },
72 { NULL, NULL, 0 }
75 struct sdl_key
77 const char* name;
78 unsigned symbol;
79 } keys_table[] = {
80 {"backspace", SDLK_BACKSPACE },
81 {"tab", SDLK_TAB },
82 {"clear", SDLK_CLEAR },
83 {"return", SDLK_RETURN },
84 {"pause", SDLK_PAUSE },
85 {"escape", SDLK_ESCAPE },
86 {"space", SDLK_SPACE },
87 {"exclaim", SDLK_EXCLAIM },
88 {"quotedbl", SDLK_QUOTEDBL },
89 {"hash", SDLK_HASH },
90 {"dollar", SDLK_DOLLAR },
91 {"ampersand", SDLK_AMPERSAND },
92 {"quote", SDLK_QUOTE },
93 {"leftparen", SDLK_LEFTPAREN },
94 {"rightparen", SDLK_RIGHTPAREN },
95 {"asterisk", SDLK_ASTERISK },
96 {"plus", SDLK_PLUS },
97 {"comma", SDLK_COMMA },
98 {"minus", SDLK_MINUS },
99 {"period", SDLK_PERIOD },
100 {"slash", SDLK_SLASH },
101 {"0", SDLK_0 },
102 {"1", SDLK_1 },
103 {"2", SDLK_2 },
104 {"3", SDLK_3 },
105 {"4", SDLK_4 },
106 {"5", SDLK_5 },
107 {"6", SDLK_6 },
108 {"7", SDLK_7 },
109 {"8", SDLK_8 },
110 {"9", SDLK_9 },
111 {"colon", SDLK_COLON },
112 {"semicolon", SDLK_SEMICOLON },
113 {"less", SDLK_LESS },
114 {"equals", SDLK_EQUALS },
115 {"greater", SDLK_GREATER },
116 {"question", SDLK_QUESTION },
117 {"at", SDLK_AT },
118 {"leftbracket", SDLK_LEFTBRACKET },
119 {"backslash", SDLK_BACKSLASH },
120 {"rightbracket", SDLK_RIGHTBRACKET },
121 {"caret", SDLK_CARET },
122 {"underscore", SDLK_UNDERSCORE },
123 {"backquote", SDLK_BACKQUOTE },
124 {"a", SDLK_a },
125 {"b", SDLK_b },
126 {"c", SDLK_c },
127 {"d", SDLK_d },
128 {"e", SDLK_e },
129 {"f", SDLK_f },
130 {"g", SDLK_g },
131 {"h", SDLK_h },
132 {"i", SDLK_i },
133 {"j", SDLK_j },
134 {"k", SDLK_k },
135 {"l", SDLK_l },
136 {"m", SDLK_m },
137 {"n", SDLK_n },
138 {"o", SDLK_o },
139 {"p", SDLK_p },
140 {"q", SDLK_q },
141 {"r", SDLK_r },
142 {"s", SDLK_s },
143 {"t", SDLK_t },
144 {"u", SDLK_u },
145 {"v", SDLK_v },
146 {"w", SDLK_w },
147 {"x", SDLK_x },
148 {"y", SDLK_y },
149 {"z", SDLK_z },
150 {"delete", SDLK_DELETE },
151 {"world_0", SDLK_WORLD_0 },
152 {"world_1", SDLK_WORLD_1 },
153 {"world_2", SDLK_WORLD_2 },
154 {"world_3", SDLK_WORLD_3 },
155 {"world_4", SDLK_WORLD_4 },
156 {"world_5", SDLK_WORLD_5 },
157 {"world_6", SDLK_WORLD_6 },
158 {"world_7", SDLK_WORLD_7 },
159 {"world_8", SDLK_WORLD_8 },
160 {"world_9", SDLK_WORLD_9 },
161 {"world_10", SDLK_WORLD_10 },
162 {"world_11", SDLK_WORLD_11 },
163 {"world_12", SDLK_WORLD_12 },
164 {"world_13", SDLK_WORLD_13 },
165 {"world_14", SDLK_WORLD_14 },
166 {"world_15", SDLK_WORLD_15 },
167 {"world_16", SDLK_WORLD_16 },
168 {"world_17", SDLK_WORLD_17 },
169 {"world_18", SDLK_WORLD_18 },
170 {"world_19", SDLK_WORLD_19 },
171 {"world_20", SDLK_WORLD_20 },
172 {"world_21", SDLK_WORLD_21 },
173 {"world_22", SDLK_WORLD_22 },
174 {"world_23", SDLK_WORLD_23 },
175 {"world_24", SDLK_WORLD_24 },
176 {"world_25", SDLK_WORLD_25 },
177 {"world_26", SDLK_WORLD_26 },
178 {"world_27", SDLK_WORLD_27 },
179 {"world_28", SDLK_WORLD_28 },
180 {"world_29", SDLK_WORLD_29 },
181 {"world_30", SDLK_WORLD_30 },
182 {"world_31", SDLK_WORLD_31 },
183 {"world_32", SDLK_WORLD_32 },
184 {"world_33", SDLK_WORLD_33 },
185 {"world_34", SDLK_WORLD_34 },
186 {"world_35", SDLK_WORLD_35 },
187 {"world_36", SDLK_WORLD_36 },
188 {"world_37", SDLK_WORLD_37 },
189 {"world_38", SDLK_WORLD_38 },
190 {"world_39", SDLK_WORLD_39 },
191 {"world_40", SDLK_WORLD_40 },
192 {"world_41", SDLK_WORLD_41 },
193 {"world_42", SDLK_WORLD_42 },
194 {"world_43", SDLK_WORLD_43 },
195 {"world_44", SDLK_WORLD_44 },
196 {"world_45", SDLK_WORLD_45 },
197 {"world_46", SDLK_WORLD_46 },
198 {"world_47", SDLK_WORLD_47 },
199 {"world_48", SDLK_WORLD_48 },
200 {"world_49", SDLK_WORLD_49 },
201 {"world_50", SDLK_WORLD_50 },
202 {"world_51", SDLK_WORLD_51 },
203 {"world_52", SDLK_WORLD_52 },
204 {"world_53", SDLK_WORLD_53 },
205 {"world_54", SDLK_WORLD_54 },
206 {"world_55", SDLK_WORLD_55 },
207 {"world_56", SDLK_WORLD_56 },
208 {"world_57", SDLK_WORLD_57 },
209 {"world_58", SDLK_WORLD_58 },
210 {"world_59", SDLK_WORLD_59 },
211 {"world_60", SDLK_WORLD_60 },
212 {"world_61", SDLK_WORLD_61 },
213 {"world_62", SDLK_WORLD_62 },
214 {"world_63", SDLK_WORLD_63 },
215 {"world_64", SDLK_WORLD_64 },
216 {"world_65", SDLK_WORLD_65 },
217 {"world_66", SDLK_WORLD_66 },
218 {"world_67", SDLK_WORLD_67 },
219 {"world_68", SDLK_WORLD_68 },
220 {"world_69", SDLK_WORLD_69 },
221 {"world_70", SDLK_WORLD_70 },
222 {"world_71", SDLK_WORLD_71 },
223 {"world_72", SDLK_WORLD_72 },
224 {"world_73", SDLK_WORLD_73 },
225 {"world_74", SDLK_WORLD_74 },
226 {"world_75", SDLK_WORLD_75 },
227 {"world_76", SDLK_WORLD_76 },
228 {"world_77", SDLK_WORLD_77 },
229 {"world_78", SDLK_WORLD_78 },
230 {"world_79", SDLK_WORLD_79 },
231 {"world_80", SDLK_WORLD_80 },
232 {"world_81", SDLK_WORLD_81 },
233 {"world_82", SDLK_WORLD_82 },
234 {"world_83", SDLK_WORLD_83 },
235 {"world_84", SDLK_WORLD_84 },
236 {"world_85", SDLK_WORLD_85 },
237 {"world_86", SDLK_WORLD_86 },
238 {"world_87", SDLK_WORLD_87 },
239 {"world_88", SDLK_WORLD_88 },
240 {"world_89", SDLK_WORLD_89 },
241 {"world_90", SDLK_WORLD_90 },
242 {"world_91", SDLK_WORLD_91 },
243 {"world_92", SDLK_WORLD_92 },
244 {"world_93", SDLK_WORLD_93 },
245 {"world_94", SDLK_WORLD_94 },
246 {"world_95", SDLK_WORLD_95 },
247 {"kp0", SDLK_KP0 },
248 {"kp1", SDLK_KP1 },
249 {"kp2", SDLK_KP2 },
250 {"kp3", SDLK_KP3 },
251 {"kp4", SDLK_KP4 },
252 {"kp5", SDLK_KP5 },
253 {"kp6", SDLK_KP6 },
254 {"kp7", SDLK_KP7 },
255 {"kp8", SDLK_KP8 },
256 {"kp9", SDLK_KP9 },
257 {"kp_period", SDLK_KP_PERIOD },
258 {"kp_divide", SDLK_KP_DIVIDE },
259 {"kp_multiply", SDLK_KP_MULTIPLY },
260 {"kp_minus", SDLK_KP_MINUS },
261 {"kp_plus", SDLK_KP_PLUS },
262 {"kp_enter", SDLK_KP_ENTER },
263 {"kp_equals", SDLK_KP_EQUALS },
264 {"up", SDLK_UP },
265 {"down", SDLK_DOWN },
266 {"right", SDLK_RIGHT },
267 {"left", SDLK_LEFT },
268 {"insert", SDLK_INSERT },
269 {"home", SDLK_HOME },
270 {"end", SDLK_END },
271 {"pageup", SDLK_PAGEUP },
272 {"pagedown", SDLK_PAGEDOWN },
273 {"f1", SDLK_F1 },
274 {"f2", SDLK_F2 },
275 {"f3", SDLK_F3 },
276 {"f4", SDLK_F4 },
277 {"f5", SDLK_F5 },
278 {"f6", SDLK_F6 },
279 {"f7", SDLK_F7 },
280 {"f8", SDLK_F8 },
281 {"f9", SDLK_F9 },
282 {"f10", SDLK_F10 },
283 {"f11", SDLK_F11 },
284 {"f12", SDLK_F12 },
285 {"f13", SDLK_F13 },
286 {"f14", SDLK_F14 },
287 {"f15", SDLK_F15 },
288 {"numlock", SDLK_NUMLOCK },
289 {"capslock", SDLK_CAPSLOCK },
290 {"scrollock", SDLK_SCROLLOCK },
291 {"rshift", SDLK_RSHIFT },
292 {"lshift", SDLK_LSHIFT },
293 {"rctrl", SDLK_RCTRL },
294 {"lctrl", SDLK_LCTRL },
295 {"ralt", SDLK_RALT },
296 {"lalt", SDLK_LALT },
297 {"rmeta", SDLK_RMETA },
298 {"lmeta", SDLK_LMETA },
299 {"lsuper", SDLK_LSUPER },
300 {"rsuper", SDLK_RSUPER },
301 {"mode", SDLK_MODE },
302 {"compose", SDLK_COMPOSE },
303 {"help", SDLK_HELP },
304 {"print", SDLK_PRINT },
305 {"sysreq", SDLK_SYSREQ },
306 {"break", SDLK_BREAK },
307 {"menu", SDLK_MENU },
308 {"power", SDLK_POWER },
309 {"euro", SDLK_EURO },
310 {"undo", SDLK_UNDO },
311 {NULL, 0 }
314 std::map<unsigned, modifier*> supported_modifiers;
315 std::map<unsigned, keygroup*> scancodekeys;
316 std::map<unsigned, keygroup*> symbolkeys;
317 #ifndef SDL_NO_JOYSTICK
318 std::map<unsigned, keygroup*> joyaxis;
319 std::map<unsigned, keygroup*> joybutton;
320 std::map<unsigned, keygroup*> joyhat;
321 #endif
323 void init_keys()
325 struct sdl_modifier* m = modifiers_table;
326 while(m->name) {
327 modifier* m2;
328 if(m->linkname)
329 m2 = new modifier(m->name, m->linkname);
330 else
331 m2 = new modifier(m->name);
332 if(m->sdlvalue)
333 supported_modifiers[m->sdlvalue] = m2;
334 m++;
336 struct sdl_key* k = keys_table;
337 while(k->name) {
338 symbolkeys[k->symbol] = new keygroup(k->name, keygroup::KT_KEY);
339 k++;
341 for(unsigned i = 0; i < 256; i++) {
342 std::ostringstream x;
343 x << "key" << i;
344 scancodekeys[i] = new keygroup(x.str(), keygroup::KT_KEY);
348 void init_joysticks()
350 #ifndef SDL_NO_JOYSTICK
351 int joysticks = SDL_NumJoysticks();
352 if(!joysticks) {
353 window::out() << "No joysticks detected." << std::endl;
354 } else {
355 window::out() << joysticks << " joystick(s) detected." << std::endl;
356 for(int i = 0; i < joysticks; i++) {
357 SDL_Joystick* j = SDL_JoystickOpen(i);
358 if(!j) {
359 window::out() << "Joystick #" << i << ": Can't open!" << std::endl;
360 continue;
362 window::out() << "Joystick #" << i << ": " << SDL_JoystickName(i) << "("
363 << SDL_JoystickNumAxes(j) << " axes, " << SDL_JoystickNumButtons(j)
364 << " buttons, " << SDL_JoystickNumHats(j) << " hats)." << std::endl;
365 for(int k = 0; k < SDL_JoystickNumAxes(j); k++) {
366 unsigned num = 256 * i + k;
367 std::ostringstream x;
368 x << "joystick" << i << "axis" << k;
369 joyaxis[num] = new keygroup(x.str(), keygroup::KT_AXIS_PAIR);
371 for(int k = 0; k < SDL_JoystickNumButtons(j); k++) {
372 unsigned num = 256 * i + k;
373 std::ostringstream x;
374 x << "joystick" << i << "button" << k;
375 joybutton[num] = new keygroup(x.str(), keygroup::KT_KEY);
377 for(int k = 0; k < SDL_JoystickNumHats(j); k++) {
378 unsigned num = 256 * i + k;
379 std::ostringstream x;
380 x << "joystick" << i << "hat" << k;
381 joyhat[num] = new keygroup(x.str(), keygroup::KT_HAT);
385 #endif
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)
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 #ifndef SDL_NO_JOYSTICK
433 } else if(e->type == SDL_JOYAXISMOTION) {
434 unsigned num = static_cast<unsigned>(e->jaxis.which) * 256 +
435 static_cast<unsigned>(e->jaxis.axis);
436 if(joyaxis.count(num))
437 joyaxis[num]->set_position(e->jaxis.value, modifiers);
438 } else if(e->type == SDL_JOYHATMOTION) {
439 unsigned num = static_cast<unsigned>(e->jhat.which) * 256 +
440 static_cast<unsigned>(e->jhat.hat);
441 short v = 0;
442 if(e->jhat.value & SDL_HAT_UP)
443 v |= 1;
444 if(e->jhat.value & SDL_HAT_RIGHT)
445 v |= 2;
446 if(e->jhat.value & SDL_HAT_DOWN)
447 v |= 4;
448 if(e->jhat.value & SDL_HAT_LEFT)
449 v |= 8;
450 if(joyhat.count(num))
451 joyhat[num]->set_position(v, modifiers);
452 } else if(e->type == SDL_JOYBUTTONDOWN || e->type == SDL_JOYBUTTONUP) {
453 unsigned num = static_cast<unsigned>(e->jbutton.which) * 256 +
454 static_cast<unsigned>(e->jbutton.button);
455 if(joybutton.count(num))
456 joybutton[num]->set_position((e->type == SDL_JOYBUTTONDOWN), modifiers);
457 #endif
459 if(identify) {
460 if(h.got_it())
461 window::modal_message(h.keys(), false);
462 keygroup::set_exclusive_key_listener(NULL);
467 extern uint32_t fontdata[];
469 namespace
471 bool SDL_initialized = false;
472 uint32_t mouse_mask = 0;
473 uint32_t vc_xoffset;
474 uint32_t vc_yoffset;
475 uint32_t vc_hscl = 1;
476 uint32_t vc_vscl = 1;
477 bool sdl_init;
478 bool modconfirm;
479 bool modal_return_flag;
480 bool delayed_close_flag;
481 std::string modmsg;
482 std::string command_buf;
483 bool command_overwrite;
484 size_t command_cursor;
485 unsigned old_screen_w;
486 unsigned old_screen_h;
487 unsigned state;
488 std::map<std::string, std::string> emustatus;
489 std::map<uint64_t, std::string> messagebuffer;
490 uint64_t messagebuffer_next_seq;
491 uint64_t messagebuffer_first_seq;
492 uint64_t messagebuffer_first_show;
493 bool console_mode;
494 uint32_t maxmessages;
495 std::list<std::string> commandhistory;
496 std::list<std::string>::iterator commandhistory_itr;
497 screen* current_screen;
498 SDL_Surface* hwsurf;
499 std::pair<uint32_t, uint32_t> current_windowsize;
500 bool pause_active;
501 uint64_t last_ui_update;
502 bool screen_is_dirty;
503 std::ofstream system_log;
504 SDL_keysym autorepeating_key;
505 unsigned autorepeat_phase = 0;
506 unsigned autorepeat_timecounter = 0;
507 numeric_setting autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
508 numeric_setting autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
511 void poll_inputs_internal() throw(std::bad_alloc);
513 namespace
515 void identify()
517 state = WINSTATE_IDENTIFY;
518 window::message("Press key to identify.");
519 window::notify_screen_update();
520 poll_inputs_internal();
523 std::string decode_string(std::string e)
525 std::string x;
526 for(size_t i = 0; i < e.length(); i += 4) {
527 char tmp[5] = {0};
528 uint32_t c1 = e[i] - 33;
529 uint32_t c2 = e[i + 1] - 33;
530 uint32_t c3 = e[i + 2] - 33;
531 uint32_t c4 = e[i + 3] - 33;
532 uint32_t c = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
533 if(c < 0x80) {
534 tmp[0] = c;
535 } else if(c < 0x800) {
536 tmp[0] = 0xC0 | (c >> 6);
537 tmp[1] = 0x80 | (c & 0x3F);
538 } else if(c < 0x10000) {
539 tmp[0] = 0xE0 | (c >> 12);
540 tmp[1] = 0x80 | ((c >> 6) & 0x3F);
541 tmp[2] = 0x80 | (c & 0x3F);
542 } else {
543 tmp[0] = 0xF0 | (c >> 18);
544 tmp[1] = 0x80 | ((c >> 12) & 0x3F);
545 tmp[2] = 0x80 | ((c >> 6) & 0x3F);
546 tmp[3] = 0x80 | (c & 0x3F);
548 x = x + tmp;
550 return x;
553 void draw_rectangle(uint8_t* data, uint32_t pitch, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
554 uint32_t color, uint32_t thickness)
556 for(uint32_t i = x1; i < x2; i++)
557 for(uint32_t j = 0; j < thickness; j++) {
558 reinterpret_cast<uint32_t*>(data + pitch * (y1 + j))[i] = color;
559 reinterpret_cast<uint32_t*>(data + pitch * (y2 - 1 - j))[i] = color;
561 for(uint32_t i = y1; i < y2; i++)
562 for(uint32_t j = 0; j < thickness; j++) {
563 reinterpret_cast<uint32_t*>(data + pitch * i)[x1 + j] = color;
564 reinterpret_cast<uint32_t*>(data + pitch * i)[x2 - 1 - j] = color;
568 std::vector<uint32_t> decode_utf8(std::string s)
570 std::vector<uint32_t> ret;
571 for(auto i = s.begin(); i != s.end(); i++) {
572 uint32_t j = static_cast<uint8_t>(*i);
573 if(j < 128)
574 ret.push_back(j);
575 else if(j < 192)
576 continue;
577 else if(j < 224) {
578 uint32_t j2 = static_cast<uint8_t>(*(++i));
579 ret.push_back((j - 192) * 64 + (j2 - 128));
580 } else if(j < 240) {
581 uint32_t j2 = static_cast<uint8_t>(*(++i));
582 uint32_t j3 = static_cast<uint8_t>(*(++i));
583 ret.push_back((j - 224) * 4096 + (j2 - 128) * 64 + (j3 - 128));
584 } else {
585 uint32_t j2 = static_cast<uint8_t>(*(++i));
586 uint32_t j3 = static_cast<uint8_t>(*(++i));
587 uint32_t j4 = static_cast<uint8_t>(*(++i));
588 ret.push_back((j - 240) * 262144 + (j2 - 128) * 4096 + (j3 - 128) * 64 + (j4 - 128));
591 return ret;
594 void draw_string(uint8_t* base, uint32_t pitch, std::vector<uint32_t> s, uint32_t x, uint32_t y,
595 uint32_t maxwidth, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
597 base += y * static_cast<size_t>(pitch) + 4 * x;
598 int32_t pos_x = 0;
599 int32_t pos_y = 0;
600 unsigned c = 0;
601 for(auto si : s) {
602 uint32_t old_x = pos_x;
603 uint32_t curstart = 16;
604 if(c == hilite_pos && hilite_mode == 1)
605 curstart = 14;
606 if(c == hilite_pos && hilite_mode == 2)
607 curstart = 0;
608 auto g = find_glyph(si, pos_x, pos_y, 0, pos_x, pos_y);
609 if(pos_y)
610 pos_x = old_x;
611 if(g.second == 0) {
612 //Empty glyph.
613 for(unsigned j = 0; j < 16; j++) {
614 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
615 for(unsigned i = 0; i < g.first && old_x + i < maxwidth; i++)
616 ptr[old_x + i] = (j >= curstart) ? 0xFFFFFFFFU : 0;
618 } else {
619 //Narrow/Wide glyph.
620 for(unsigned j = 0; j < 16; j++) {
621 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
622 uint32_t dataword = fontdata[g.second + j / 4];
623 for(uint32_t i = 0; i < g.first && old_x + i < maxwidth; i++) {
624 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
625 1));
626 b ^= (j >= curstart);
627 ptr[old_x + i] = b ? 0xFFFFFFFFU : 0;
631 c++;
633 for(unsigned j = 0; j < 16; j++) {
634 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
635 uint32_t curstart = 16;
636 if(c == hilite_pos && hilite_mode == 1)
637 curstart = 14;
638 if(c == hilite_pos && hilite_mode == 2)
639 curstart = 0;
640 for(uint32_t i = pos_x; i < maxwidth; i++)
641 ptr[i] = ((i - pos_x) < 8 && j >= curstart) ? 0xFFFFFFFFU : 0;
645 void draw_string(uint8_t* base, uint32_t pitch, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth,
646 uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
648 draw_string(base, pitch, decode_utf8(s), x, y, maxwidth, hilite_mode, hilite_pos);
651 void draw_command(uint8_t* base, uint32_t pitch, std::string s, size_t cursor, uint32_t x, uint32_t y,
652 uint32_t maxwidth, bool overwrite)
654 //FIXME, scroll text if too long.
655 uint32_t hilite_mode = overwrite ? 2 : 1;
656 auto s2 = decode_utf8(s);
657 draw_string(base, pitch, s2, x, y, maxwidth, hilite_mode, cursor);
660 void draw_modal_dialog(SDL_Surface* surf, std::string msg, bool confirm)
662 int32_t pos_x = 0;
663 int32_t pos_y = 0;
664 uint32_t width = 0;
665 uint32_t height = 0;
666 if(confirm)
667 msg = msg + "\n\nHit Enter to confirm, Esc to cancel";
668 else
669 msg = msg + "\n\nHit Enter or Esc to dismiss";
670 auto s2 = decode_utf8(msg);
671 for(auto i : s2) {
672 auto g = find_glyph(i, pos_x, pos_y, 0, pos_x, pos_y);
673 if(pos_x + g.first > width)
674 width = static_cast<uint32_t>(pos_x + g.first);
675 if(pos_y + 16 > static_cast<int32_t>(height))
676 height = static_cast<uint32_t>(pos_y + 16);
678 uint32_t x1;
679 uint32_t x2;
680 uint32_t y1;
681 uint32_t y2;
682 if(width + 12 >= static_cast<uint32_t>(surf->w)) {
683 x1 = 6;
684 x2 = surf->w - 6;
685 width = x2 - x1;
686 } else {
687 x1 = (surf->w - width) / 2;
688 x2 = x1 + width;
690 if(height + 12 >= static_cast<uint32_t>(surf->h)) {
691 y1 = 6;
692 y2 = surf->h - 6;
693 height = y2 - y1;
694 } else {
695 y1 = (surf->h - height) / 2;
696 y2 = y1 + height;
698 for(uint32_t j = y1 - 6; j < y2 + 6; j++)
699 memset(reinterpret_cast<uint8_t*>(surf->pixels) + j * surf->pitch + 4 * (x1 - 6), 0,
700 4 * (x2 - x1 + 12));
701 uint32_t bordercolor = (0xFF << surf->format->Rshift) | (0x80 << surf->format->Gshift);
702 draw_rectangle(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, x1 - 4, y1 - 4, x2 + 4, y2 + 4,
703 bordercolor, 2);
705 pos_x = 0;
706 pos_y = 0;
707 for(auto i : s2) {
708 uint32_t ox = pos_x;
709 uint32_t oy = pos_y;
710 auto g = find_glyph(i, pos_x, pos_y, 0, pos_x, pos_y);
711 if(static_cast<uint32_t>(pos_y) > height)
712 break;
713 uint8_t* base = reinterpret_cast<uint8_t*>(surf->pixels) + (y1 + oy) * surf->pitch +
714 4 * (x1 + ox);
715 if(g.second) {
716 //Narrow/Wide glyph.
717 for(unsigned j = 0; j < 16; j++) {
718 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + surf->pitch * j);
719 uint32_t dataword = fontdata[g.second + j / 4];
720 for(uint32_t i = 0; i < g.first && (ox + i) < width; i++) {
721 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
722 1));
723 ptr[i] = b ? bordercolor : 0;
730 void do_keyboard_command_edit(SDL_keysym k)
732 //These are not command edit!
733 if(k.sym == SDLK_ESCAPE)
734 return;
735 if(k.sym == SDLK_RETURN)
736 return;
737 if(k.sym == SDLK_KP_ENTER)
738 return;
739 //Map keys a bit if numlock is off.
740 if((k.mod & KMOD_NUM) == 0) {
741 switch(k.sym) {
742 case SDLK_KP0: k.sym = SDLK_INSERT; break;
743 case SDLK_KP1: k.sym = SDLK_END; break;
744 case SDLK_KP2: k.sym = SDLK_DOWN; break;
745 case SDLK_KP3: k.sym = SDLK_PAGEDOWN; break;
746 case SDLK_KP4: k.sym = SDLK_LEFT; break;
747 case SDLK_KP5: return;
748 case SDLK_KP6: k.sym = SDLK_RIGHT; break;
749 case SDLK_KP7: k.sym = SDLK_HOME; break;
750 case SDLK_KP8: k.sym = SDLK_UP; break;
751 case SDLK_KP9: k.sym = SDLK_PAGEUP; break;
752 case SDLK_KP_PERIOD: k.sym = SDLK_DELETE; break;
753 default:
754 break;
757 //Special editing operations.
758 switch(k.sym) {
759 case SDLK_INSERT:
760 command_overwrite = !command_overwrite;
761 window::notify_screen_update();
762 return;
763 case SDLK_END:
764 command_cursor = command_buf.length();
765 window::notify_screen_update();
766 return;
767 case SDLK_DOWN:
768 case SDLK_PAGEDOWN:
769 if(commandhistory_itr != commandhistory.begin()) {
770 commandhistory_itr--;
771 command_buf = *commandhistory_itr;
772 if(command_cursor > command_buf.length())
773 command_cursor = command_buf.length();
775 window::notify_screen_update();
776 return;
777 case SDLK_LEFT:
778 command_cursor = (command_cursor > 0) ? (command_cursor - 4) : 0;
779 window::notify_screen_update();
780 return;
781 case SDLK_RIGHT:
782 command_cursor = (command_cursor < command_buf.length()) ? (command_cursor + 4) :
783 command_buf.length();
784 window::notify_screen_update();
785 return;
786 case SDLK_HOME:
787 command_cursor = 0;
788 window::notify_screen_update();
789 return;
790 case SDLK_UP:
791 case SDLK_PAGEUP: {
792 auto tmp = commandhistory_itr;
793 if(++tmp != commandhistory.end()) {
794 commandhistory_itr++;
795 command_buf = *commandhistory_itr;
796 if(command_cursor > command_buf.length())
797 command_cursor = command_buf.length();
799 window::notify_screen_update();
800 return;
802 case SDLK_DELETE:
803 if(command_cursor < command_buf.length())
804 command_buf = command_buf.substr(0, command_cursor) +
805 command_buf.substr(command_cursor + 4);
806 window::notify_screen_update();
807 *commandhistory_itr = command_buf;
808 return;
809 case SDLK_BACKSPACE:
810 if(command_cursor > 0) {
811 command_buf = command_buf.substr(0, command_cursor - 4) +
812 command_buf.substr(command_cursor);
813 command_cursor -= 4;
815 window::notify_screen_update();
816 *commandhistory_itr = command_buf;
817 return;
818 default:
819 break;
822 //Not a special editing operation, insert/overwrite a character.
823 uint32_t code = k.unicode;
824 if(!code)
825 return;
826 uint8_t c1 = 33 + ((code >> 18) & 0x3F);
827 uint8_t c2 = 33 + ((code >> 12) & 0x3F);
828 uint8_t c3 = 33 + ((code >> 6) & 0x3F);
829 uint8_t c4 = 33 + (code & 0x3F);
830 if(command_overwrite && command_cursor < command_buf.length()) {
831 command_buf[command_cursor] = c1;
832 command_buf[command_cursor + 1] = c2;
833 command_buf[command_cursor + 2] = c3;
834 command_buf[command_cursor + 3] = c4;
835 command_cursor += 4;
836 } else {
837 std::string foo = " ";
838 foo[0] = c1;
839 foo[1] = c2;
840 foo[2] = c3;
841 foo[3] = c4;
842 command_buf = command_buf.substr(0, command_cursor) + foo + command_buf.substr(command_cursor);
843 command_cursor += 4;
845 *commandhistory_itr = command_buf;
846 window::notify_screen_update();
849 void do_event(SDL_Event& e) throw(std::bad_alloc)
851 #ifdef SIGALRM
852 alarm(WATCHDOG_TIMEOUT);
853 #endif
854 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE && e.key.keysym.mod == (KMOD_LCTRL |
855 KMOD_LALT))
856 exit(1);
857 if(e.type == SDL_USEREVENT && e.user.code == 0) {
858 if(screen_is_dirty)
859 window::notify_screen_update();
861 SDLKey key;
862 if(e.type == SDL_ACTIVEEVENT && e.active.gain && e.active.state == SDL_APPACTIVE) {
863 window::notify_screen_update();
864 return;
866 if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
867 key = e.key.keysym.sym;
869 if(e.type == SDL_QUIT && state == WINSTATE_IDENTIFY)
870 return;
871 if(e.type == SDL_QUIT && state == WINSTATE_MODAL) {
872 delayed_close_flag = true;
873 return;
875 if(e.type == SDL_QUIT) {
876 window_callback::do_close();
877 state = WINSTATE_NORMAL;
878 return;
881 switch(state) {
882 case WINSTATE_NORMAL:
883 if(e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
884 int32_t xc = e.button.x;
885 int32_t yc = e.button.y;
886 xc = (xc - 6 - vc_xoffset) / vc_hscl;
887 yc = (yc - 6 - vc_yoffset) / vc_vscl;
888 if(e.button.button == SDL_BUTTON_LEFT) {
889 if(e.button.state == SDL_PRESSED)
890 mouse_mask |= 1;
891 else
892 mouse_mask &= ~1;
894 if(e.button.button == SDL_BUTTON_MIDDLE) {
895 if(e.button.state == SDL_PRESSED)
896 mouse_mask |= 2;
897 else
898 mouse_mask &= ~2;
900 if(e.button.button == SDL_BUTTON_RIGHT) {
901 if(e.button.state == SDL_PRESSED)
902 mouse_mask |= 4;
903 else
904 mouse_mask &= ~4;
906 window_callback::do_click(xc, yc, mouse_mask);
908 if(e.type == SDL_KEYDOWN && key == SDLK_ESCAPE)
909 return;
910 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
911 state = WINSTATE_COMMAND;
912 command_buf = "";
913 command_cursor = 0;
914 commandhistory.push_front("");
915 if(commandhistory.size() > MAXHISTORY)
916 commandhistory.pop_back();
917 commandhistory_itr = commandhistory.begin();
918 window::notify_screen_update();
919 poll_inputs_internal();
920 return;
922 process_input_event(&e, false);
923 break;
924 case WINSTATE_MODAL:
925 //Send the key and eat it (prevent input from getting confused).
926 keygroup::set_exclusive_key_listener(&keyeater);
927 process_input_event(&e, false),
928 keygroup::set_exclusive_key_listener(NULL);
929 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
930 state = WINSTATE_NORMAL;
931 modconfirm = false;
932 modal_return_flag = true;
933 modmsg = "";
934 window::notify_screen_update(true);
935 return;
937 if(e.type == SDL_KEYUP && (key == SDLK_RETURN || key == SDLK_KP_ENTER)) {
938 state = WINSTATE_NORMAL;
939 modal_return_flag = true;
940 modmsg = "";
941 window::notify_screen_update(true);
942 return;
944 break;
945 case WINSTATE_COMMAND:
946 //Send the key and eat it (prevent input from getting confused).
947 keygroup::set_exclusive_key_listener(&keyeater);
948 process_input_event(&e, false),
949 keygroup::set_exclusive_key_listener(NULL);
950 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) {
951 state = WINSTATE_NORMAL;
952 command_buf = "";
953 window::notify_screen_update();
954 if(commandhistory.front() == "")
955 commandhistory.pop_front();
956 return;
958 if(e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_RETURN ||
959 e.key.keysym.sym == SDLK_KP_ENTER)) {
960 state = WINSTATE_NORMAL;
961 if(commandhistory.front() == "")
962 commandhistory.pop_front();
963 command::invokeC(decode_string(command_buf));
964 command_buf = "";
965 window::notify_screen_update();
966 autorepeat_phase = 0;
967 return;
969 if(e.type == SDL_KEYDOWN) {
970 autorepeating_key = e.key.keysym;
971 autorepeat_phase = 1;
972 autorepeat_timecounter = 0;
973 do_keyboard_command_edit(e.key.keysym);
974 } else if(e.type == SDL_KEYUP) {
975 autorepeat_phase = 0;
977 if(e.type == SDL_USEREVENT && e.user.code == 0) {
978 autorepeat_timecounter++;
979 if(!autorepeat_phase)
980 break;
981 unsigned timeout = (autorepeat_phase == 1) ? autorepeat_first : autorepeat_subsequent;
982 if(autorepeat_timecounter >= timeout) {
983 do_keyboard_command_edit(autorepeating_key);
984 autorepeat_timecounter = 0;
985 autorepeat_phase = 2;
988 break;
989 case WINSTATE_IDENTIFY:
990 process_input_event(&e, true);
991 break;
996 void graphics_init()
998 SDL_initialized = true;
999 #ifdef SIGALRM
1000 signal(SIGALRM, sigalrm_handler);
1001 alarm(WATCHDOG_TIMEOUT);
1002 #endif
1003 init_keys();
1004 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
1005 time_t curtime = __real_time(NULL);
1006 struct tm* tm = localtime(&curtime);
1007 char buffer[1024];
1008 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1009 system_log << "-----------------------------------------------------------------------" << std::endl;
1010 system_log << "lsnes started at " << buffer << std::endl;
1011 system_log << "-----------------------------------------------------------------------" << std::endl;
1012 if(!sdl_init) {
1013 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
1014 SDL_EnableUNICODE(true);
1015 sdl_init = true;
1016 tid = SDL_AddTimer(MIN_UPDATE_TIME / 1000 + 1, timer_cb, NULL);
1018 state = WINSTATE_NORMAL;
1019 current_screen = NULL;
1020 pause_active = false;
1021 hwsurf = NULL;
1022 command_overwrite = false;
1023 old_screen_h = 0;
1024 old_screen_w = 0;
1025 modal_return_flag = false;
1026 delayed_close_flag = false;
1027 messagebuffer_next_seq = 0;
1028 messagebuffer_first_seq = 0;
1029 messagebuffer_first_show = 0;
1030 console_mode = false;
1031 maxmessages = MAXMESSAGES;
1033 window::notify_screen_update();
1034 std::string windowname = "lsnes-" + lsnes_version + "[" + bsnes_core_version + "]";
1035 SDL_WM_SetCaption(windowname.c_str(), "lsnes");
1037 init_joysticks();
1040 void graphics_quit()
1042 time_t curtime = time(NULL);
1043 struct tm* tm = localtime(&curtime);
1044 char buffer[1024];
1045 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1046 system_log << "-----------------------------------------------------------------------" << std::endl;
1047 system_log << "lsnes shutting down at " << buffer << std::endl;
1048 system_log << "-----------------------------------------------------------------------" << std::endl;
1049 system_log.close();
1050 if(sdl_init) {
1051 SDL_RemoveTimer(tid);
1052 SDL_Quit();
1053 sdl_init = false;
1055 SDL_initialized = false;
1058 bool window::modal_message(const std::string& msg, bool confirm) throw(std::bad_alloc)
1060 modconfirm = confirm;
1061 modmsg = msg;
1062 state = WINSTATE_MODAL;
1063 notify_screen_update();
1064 poll_inputs_internal();
1065 bool ret = modconfirm;
1066 if(delayed_close_flag) {
1067 delayed_close_flag = false;
1068 window_callback::do_close();
1070 return ret;
1073 void window::message(const std::string& msg) throw(std::bad_alloc)
1075 std::string msg2 = msg;
1076 bool locked_mode = (messagebuffer_next_seq - messagebuffer_first_show <= maxmessages) ;
1077 while(msg2 != "") {
1078 size_t s = msg2.find_first_of("\n");
1079 std::string forlog;
1080 if(s >= msg2.length()) {
1081 if(SDL_initialized) {
1082 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2);
1083 system_log << forlog << std::endl;
1084 } else
1085 std::cerr << msg2 << std::endl;
1086 break;
1087 } else {
1088 if(SDL_initialized) {
1089 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2.substr(0, s));
1090 system_log << forlog << std::endl;
1091 } else
1092 std::cerr << msg2.substr(0, s) << std::endl;
1093 msg2 = msg2.substr(s + 1);
1097 if(locked_mode && messagebuffer_first_show + maxmessages < messagebuffer_next_seq)
1098 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1100 while(messagebuffer.size() > MSGHISTORY) {
1101 messagebuffer.erase(messagebuffer_first_seq++);
1102 if(messagebuffer_first_show < messagebuffer_first_seq)
1103 messagebuffer_first_show = messagebuffer_first_seq;
1105 notify_screen_update();
1108 void window::set_main_surface(screen& scr) throw()
1110 current_screen = &scr;
1111 notify_screen_update(true);
1114 namespace
1116 bool is_time_for_screen_update(bool full)
1118 uint64_t curtime = get_utime();
1119 //Always do full updates.
1120 if(!full && last_ui_update < curtime && last_ui_update + MIN_UPDATE_TIME > curtime) {
1121 screen_is_dirty = true;
1122 return false;
1124 last_ui_update = curtime;
1125 screen_is_dirty = false;
1126 return true;
1129 std::pair<uint32_t, uint32_t> compute_screen_size(uint32_t width, uint32_t height)
1131 if(width < 512)
1132 width = 512;
1133 if(height < 448)
1134 height = 448;
1135 return std::make_pair(width, height);
1138 std::pair<uint32_t, uint32_t> compute_window_size(uint32_t width, uint32_t height)
1140 auto g = compute_screen_size(width, height);
1141 uint32_t win_w = ((g.first + 15) >> 4 << 4) + 278;
1142 uint32_t win_h = g.second + MAXMESSAGES * 16 + 48;
1143 return std::make_pair(win_w, win_h);
1146 void show_fps()
1148 try {
1149 std::ostringstream y;
1150 y << get_framerate();
1151 emustatus["FPS"] = y.str();
1152 } catch(...) {
1156 void redraw_borders(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize,
1157 std::pair<uint32_t, uint32_t> windowsize)
1159 //Blank the screen and draw borders.
1160 memset(swsurf->pixels, 0, windowsize.second * swsurf->pitch);
1161 uint32_t bordercolor = 0xFF << (swsurf->format->Gshift);
1162 uint32_t msgbox_min_x = 2;
1163 uint32_t msgbox_min_y = 2;
1164 uint32_t msgbox_max_x = windowsize.first - 2;
1165 uint32_t msgbox_max_y = windowsize.second - 28;
1166 uint32_t cmdbox_min_x = 2;
1167 uint32_t cmdbox_max_x = windowsize.first - 2;
1168 uint32_t cmdbox_min_y = windowsize.second - 26;
1169 uint32_t cmdbox_max_y = windowsize.second - 2;
1170 if(!console_mode) {
1171 uint32_t scrbox_min_x = 2;
1172 uint32_t scrbox_max_x = screensize.first + 10;
1173 uint32_t scrbox_min_y = 2;
1174 uint32_t scrbox_max_y = screensize.second + 10;
1175 uint32_t stsbox_min_x = screensize.first + 12;
1176 uint32_t stsbox_max_x = windowsize.first - 2;
1177 uint32_t stsbox_min_y = 2;
1178 uint32_t stsbox_max_y = screensize.second + 10;
1179 msgbox_min_y = screensize.second + 12;
1180 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, scrbox_min_x,
1181 scrbox_min_y, scrbox_max_x, scrbox_max_y, bordercolor, 2);
1182 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, stsbox_min_x,
1183 stsbox_min_y, stsbox_max_x, stsbox_max_y, bordercolor, 2);
1185 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, msgbox_min_x,
1186 msgbox_min_y, msgbox_max_x, msgbox_max_y, bordercolor, 2);
1187 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, cmdbox_min_x,
1188 cmdbox_min_y, cmdbox_max_x, cmdbox_max_y, bordercolor, 2);
1191 void draw_main_screen(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize)
1193 uint32_t cw = current_screen ? current_screen->width : 0;
1194 uint32_t ch = current_screen ? current_screen->height : 0;
1195 //Blank parts not drawn.
1196 for(uint32_t i = 6; i < ch + 6; i++)
1197 memset(reinterpret_cast<uint8_t*>(swsurf->pixels) + i * swsurf->pitch + 24 + 4 * cw, 0,
1198 4 * (screensize.first - cw));
1199 for(uint32_t i = ch + 6; i < screensize.second + 6; i++)
1200 memset(reinterpret_cast<uint8_t*>(swsurf->pixels) + i * swsurf->pitch + 24, 0,
1201 4 * screensize.first);
1202 if(current_screen) {
1203 for(uint32_t i = 0; i < ch; i++)
1204 memcpy(reinterpret_cast<uint8_t*>(swsurf->pixels) + (i + 6) * swsurf->pitch + 24,
1205 reinterpret_cast<uint8_t*>(current_screen->memory) + current_screen->pitch * i,
1206 4 * cw);
1210 void draw_status_area(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize)
1212 uint32_t status_x = screensize.first + 16;
1213 uint32_t status_y = 6;
1214 for(auto i : emustatus) {
1215 std::string msg = i.first + " " + i.second;
1216 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, msg, status_x, status_y,
1217 256);
1218 status_y += 16;
1220 while(status_y - 6 < screensize.second / 16 * 16) {
1221 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "", status_x, status_y,
1222 256);
1223 status_y += 16;
1227 void draw_messages(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> screensize,
1228 std::pair<uint32_t, uint32_t> windowsize)
1230 uint32_t message_y;
1231 if(!console_mode)
1232 message_y = screensize.second + 16;
1233 else
1234 message_y = 6;
1235 for(size_t j = 0; j < maxmessages; j++)
1236 try {
1237 std::ostringstream o;
1238 if(messagebuffer_first_show + j < messagebuffer_next_seq)
1239 o << (messagebuffer_first_show + j + 1) << ": "
1240 << messagebuffer[messagebuffer_first_show + j];
1241 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, o.str(), 6,
1242 message_y + 16 * j, windowsize.first - 12);
1243 } catch(...) {
1245 if(messagebuffer_next_seq - messagebuffer_first_show > maxmessages)
1246 try {
1247 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "--More--",
1248 windowsize.first - 76, message_y + 16 * maxmessages - 16, 64);
1249 } catch(...) {
1253 void draw_command(SDL_Surface* swsurf, std::pair<uint32_t, uint32_t> windowsize)
1255 uint32_t command_y = windowsize.second - 22;
1256 try {
1257 std::string command_showas = decode_string(command_buf);
1258 if(state == WINSTATE_COMMAND)
1259 draw_command(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, command_showas,
1260 command_cursor / 4, 6, command_y, windowsize.first - 12, command_overwrite);
1261 else
1262 draw_string(reinterpret_cast<uint8_t*>(swsurf->pixels), swsurf->pitch, "", 6,
1263 command_y, windowsize.first - 12);
1264 } catch(...) {
1269 void window::notify_screen_update(bool full) throw()
1271 uint64_t tnow = get_utime();
1272 bool resize_screen = false;
1273 if(!is_time_for_screen_update(full)) {
1274 return;
1276 auto windowsize = compute_window_size(current_screen ? current_screen->width : 0, current_screen ?
1277 current_screen->height : 0);
1278 auto screensize = compute_screen_size(current_screen ? current_screen->width : 0, current_screen ?
1279 current_screen->height : 0);
1280 show_fps();
1283 if(!hwsurf || windowsize != current_windowsize) {
1284 //Create/Resize the window.
1285 SDL_Surface* hwsurf2 = SDL_SetVideoMode(windowsize.first, windowsize.second, 32, SDL_SWSURFACE);
1286 if(!hwsurf2) {
1287 //We are in too fucked up state to even print error as message.
1288 std::cout << "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl;
1289 exit(1);
1291 hwsurf = hwsurf2;
1292 full = true;
1293 current_windowsize = windowsize;
1295 if(current_screen)
1296 current_screen->set_palette(hwsurf->format->Rshift, hwsurf->format->Gshift, hwsurf->format->Bshift);
1297 SDL_LockSurface(hwsurf);
1298 if(full)
1299 redraw_borders(hwsurf, screensize, windowsize);
1300 if(!console_mode) {
1301 draw_main_screen(hwsurf, screensize);
1302 draw_status_area(hwsurf, screensize);
1304 draw_messages(hwsurf, screensize, windowsize);
1305 draw_command(hwsurf, windowsize);
1307 //Draw modal dialog.
1308 if(state == WINSTATE_MODAL)
1309 try {
1310 draw_modal_dialog(hwsurf, modmsg, modconfirm);
1311 } catch(...) {
1313 SDL_UnlockSurface(hwsurf);
1314 //SDL_BlitSurface(swsurf, NULL, hwsurf, NULL);
1315 SDL_UpdateRect(hwsurf, 0, 0, 0, 0);
1316 in_paint_time += (get_utime() - tnow);
1319 void poll_inputs_internal() throw(std::bad_alloc)
1321 SDL_Event e;
1322 while(state != WINSTATE_NORMAL) {
1323 poll_joysticks();
1324 if(SDL_PollEvent(&e))
1325 do_event(e);
1326 ::wait_usec(10000);
1327 if(delayed_close_flag) {
1328 state = WINSTATE_NORMAL;
1329 return;
1334 void window::poll_inputs() throw(std::bad_alloc)
1336 SDL_Event e;
1337 while(1) {
1338 assert(state == WINSTATE_NORMAL);
1339 poll_joysticks();
1340 if(!pause_active && !SDL_PollEvent(&e))
1341 break;
1342 else if(!pause_active)
1343 do_event(e);
1344 else if(SDL_PollEvent(&e))
1345 do_event(e);
1346 else
1347 ::wait_usec(10000);
1351 std::map<std::string, std::string>& window::get_emustatus() throw()
1353 return emustatus;
1356 void window::paused(bool enable) throw()
1358 pause_active = enable;
1359 notify_screen_update();
1362 namespace
1364 function_ptr_command<> identify_key("identify-key", "Identify a key",
1365 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1366 []() throw(std::bad_alloc, std::runtime_error) {
1367 identify();
1370 function_ptr_command<> scroll_up("scroll-up", "Scroll messages a page up",
1371 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1372 []() throw(std::bad_alloc, std::runtime_error) {
1373 if(messagebuffer_first_show > maxmessages)
1374 messagebuffer_first_show -= maxmessages;
1375 else
1376 messagebuffer_first_show = 0;
1377 if(messagebuffer_first_show < messagebuffer_first_seq)
1378 messagebuffer_first_show = messagebuffer_first_seq;
1379 window::notify_screen_update();
1382 function_ptr_command<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1383 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1384 []() throw(std::bad_alloc, std::runtime_error) {
1385 messagebuffer_first_show = messagebuffer_first_seq;
1386 window::notify_screen_update();
1389 function_ptr_command<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1390 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1391 []() throw(std::bad_alloc, std::runtime_error) {
1392 if(messagebuffer_next_seq < maxmessages)
1393 messagebuffer_first_show = 0;
1394 else
1395 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1396 window::notify_screen_update();
1399 function_ptr_command<> scrolldown("scroll-down", "Scroll messages a page down",
1400 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1401 []() throw(std::bad_alloc, std::runtime_error) {
1402 messagebuffer_first_show += maxmessages;
1403 if(messagebuffer_next_seq < maxmessages)
1404 messagebuffer_first_show = 0;
1405 else if(messagebuffer_next_seq < messagebuffer_first_show + maxmessages)
1406 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1407 window::notify_screen_update();
1410 function_ptr_command<> toggle_console("toggle-console", "Toggle console between small and full window",
1411 "Syntax: toggle-console\nToggles console between small and large.\n",
1412 []() throw(std::bad_alloc, std::runtime_error) {
1413 console_mode = !console_mode;
1414 if(console_mode)
1415 maxmessages = hwsurf ? (hwsurf->h - 38) / 16 : 36;
1416 else
1417 maxmessages = MAXMESSAGES;
1418 if(messagebuffer_next_seq < maxmessages)
1419 messagebuffer_first_show = 0;
1420 else
1421 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1422 window::notify_screen_update(true);
1426 void window::wait_usec(uint64_t usec) throw(std::bad_alloc)
1428 wait_canceled = false;
1429 uint64_t end_at = get_utime() + usec;
1430 while(!wait_canceled) {
1431 SDL_Event e;
1432 poll_joysticks();
1433 while(SDL_PollEvent(&e))
1434 do_event(e);
1435 uint64_t curtime = get_utime();
1436 if(curtime > end_at || wait_canceled)
1437 break;
1438 if(end_at > curtime + 10000)
1439 ::wait_usec(10000);
1440 else
1441 ::wait_usec(end_at - curtime);
1443 wait_canceled = false;
1446 void window::fatal_error() throw()
1448 try {
1449 message("PANIC: Cannot continue, press ESC or close window to exit.");
1450 //Force redraw.
1451 notify_screen_update(true);
1452 } catch(...) {
1453 //Just crash.
1454 exit(1);
1456 time_t curtime = time(NULL);
1457 struct tm* tm = localtime(&curtime);
1458 char buffer[1024];
1459 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1460 system_log << "-----------------------------------------------------------------------" << std::endl;
1461 system_log << "lsnes paniced at " << buffer << std::endl;
1462 system_log << "-----------------------------------------------------------------------" << std::endl;
1463 system_log.close();
1464 while(true) {
1465 SDL_Event e;
1466 if(SDL_WaitEvent(&e)) {
1467 if(e.type == SDL_QUIT)
1468 exit(1);
1469 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
1470 exit(1);
1475 void window::cancel_wait() throw()
1477 wait_canceled = true;
1480 void window::set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_t hscl, uint32_t vscl)
1482 vc_xoffset = xoffset;
1483 vc_yoffset = yoffset;
1484 vc_hscl = hscl;
1485 vc_vscl = vscl;
1488 #ifndef SDL_NO_JOYSTICK
1489 void poll_joysticks()
1491 //We poll it in event loop for SDL.
1494 const char* joystick_plugin_name = "SDL joystick plugin";
1495 #endif
1497 const char* graphics_plugin_name = "SDL graphics plugin";