Add large-video option to force hires dumping
[lsnes.git] / window-sdl.cpp
blobaab8294df48e542a1269b265bb5eb1ce97ab76d0
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 "fieldsplit.hpp"
13 #include <sstream>
14 #include <fstream>
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
35 class keymapper_helper_sdl
37 public:
38 typedef struct {
39 int device;
40 SDL_keysym keyboard;
41 uint32_t joyaxis;
42 uint32_t joybutton;
43 uint32_t joyhat;
44 } keysymbol;
45 struct internal_keysymbol
47 int mode;
48 unsigned scan;
49 unsigned symbolic;
50 unsigned joyaxis;
51 unsigned joyhat;
52 unsigned joybutton;
53 bool operator==(const internal_keysymbol& k) const;
55 struct translated_event
57 translated_event(keysymbol _symbol, bool _polarity, bool _more = false);
58 keysymbol symbol;
59 bool polarity;
60 bool more;
62 static translated_event translate_event(SDL_Event& e) throw(std::bad_alloc, std::runtime_error);
63 static unsigned mod_str(std::string mod) throw(std::bad_alloc, std::runtime_error);
64 static unsigned mod_key(keysymbol key) throw(std::bad_alloc);
65 static internal_keysymbol key_str(std::string key) throw(std::bad_alloc, std::runtime_error);
66 static internal_keysymbol key_key(keysymbol key) throw(std::bad_alloc);
67 static std::string name_key(unsigned mod, unsigned modmask, struct internal_keysymbol key)
68 throw(std::bad_alloc);
69 static std::string print_key_info(keysymbol key) throw(std::bad_alloc);
72 #define KEYTYPE_SCAN_SYMBOL 0
73 #define KEYTYPE_SCAN 1
74 #define KEYTYPE_SYMBOL 2
75 #define KEYTYPE_JOYAXIS 3
76 #define KEYTYPE_JOYHAT 4
77 #define KEYTYPE_JOYBUTTON 5
78 #define KEYTYPE_NONE 6
80 #define BARE_CTRL 0x0001
81 #define BARE_LCTRL 0x0002
82 #define BARE_RCTRL 0x0004
83 #define BARE_NUM 0x0008
84 #define BARE_SHIFT 0x0010
85 #define BARE_LSHIFT 0x0020
86 #define BARE_RSHIFT 0x0040
87 #define BARE_CAPS 0x0080
88 #define BARE_ALT 0x0100
89 #define BARE_LALT 0x0200
90 #define BARE_RALT 0x0400
91 #define BARE_MODE 0x0800
92 #define BARE_META 0x1000
93 #define BARE_LMETA 0x2000
94 #define BARE_RMETA 0x4000
96 #define LCTRL_VALUE (BARE_CTRL | BARE_LCTRL)
97 #define RCTRL_VALUE (BARE_CTRL | BARE_RCTRL)
98 #define LALT_VALUE (BARE_ALT | BARE_LALT)
99 #define RALT_VALUE (BARE_ALT | BARE_RALT)
100 #define LSHIFT_VALUE (BARE_SHIFT | BARE_LSHIFT)
101 #define RSHIFT_VALUE (BARE_SHIFT | BARE_RSHIFT)
102 #define LMETA_VALUE (BARE_META | BARE_LMETA)
103 #define RMETA_VALUE (BARE_META | BARE_RMETA)
105 struct calibration
107 calibration();
108 short left;
109 short center;
110 short right;
111 short temp;
112 bool known;
113 void update(short val) throw();
114 void centered() throw();
115 short analog(short val) throw();
116 short digital(short val) throw();
119 calibration::calibration()
121 known = false;
124 void calibration::update(short val) throw()
126 if(!known) {
127 temp = val;
128 left = val;
129 center = val;
130 right = val;
131 known = true;
132 return;
134 temp = val;
135 if(temp > right)
136 right = temp;
137 if(temp < left)
138 left = temp;
141 void calibration::centered() throw()
143 center = temp;
146 short calibration::analog(short val) throw()
148 if(!known)
149 return 0;
150 if(val <= left)
151 return -32768;
152 if(val >= right)
153 return 32767;
154 if(val < center)
155 return static_cast<short>(-32768.0 * (val - left) / (center - left));
156 if(val > center)
157 return static_cast<short>(32767.0 * (val - center) / (right - center));
158 return 0;
161 short calibration::digital(short val) throw()
163 short a = analog(val);
164 if(a < -JOYTHRESHOLD)
165 return -1;
166 if(a > JOYTHRESHOLD)
167 return 1;
168 return 0;
171 std::map<unsigned short, calibration> calibrations;
173 void handle_calibration_event(SDL_JoyAxisEvent& e)
175 unsigned short num = static_cast<unsigned short>(e.which) * 256 + static_cast<unsigned short>(e.axis);
176 calibrations[num].update(e.value);
179 void exit_calibration()
181 for(auto i = calibrations.begin(); i != calibrations.end(); i++)
182 i->second.centered();
185 namespace
187 bool wait_canceled;
188 SDL_TimerID tid;
189 std::map<unsigned short, int> axis_current_state; //-1, 0 or 1.
190 std::map<unsigned short, unsigned> hat_current_state; //1 for up, 2 for right, 4 for down, 8 for left.
192 void sigalrm_handler(int s)
194 _exit(1);
197 Uint32 timer_cb(Uint32 interval, void* param)
199 SDL_Event e;
200 e.type = SDL_USEREVENT;
201 e.user.code = 0;
202 SDL_PushEvent(&e);
203 return interval;
206 struct modifier
208 const char* name;
209 bool synthethic;
210 unsigned sdlvalue;
211 unsigned asrealkey;
212 unsigned asmodifier;
213 } modifiers_table[] = {
214 { "ctrl", true, 0, 0, 0x0001 },
215 { "lctrl", false, KMOD_LCTRL, 0x0003, 0x0002 },
216 { "rctrl", false, KMOD_RCTRL, 0x0005, 0x0004 },
217 { "alt", true, 0, 0, 0x0008 },
218 { "lalt", false, KMOD_LALT, 0x0018, 0x0010 },
219 { "ralt", false, KMOD_RALT, 0x0028, 0x0020 },
220 { "shift", true, 0, 0, 0x0040 },
221 { "lshift", false, KMOD_LSHIFT, 0x00C0, 0x0080 },
222 { "rshift", false, KMOD_RSHIFT, 0x0140, 0x0100 },
223 { "meta", true, 0, 0, 0x0200 },
224 { "lmeta", false, KMOD_LMETA, 0x0600, 0x0400 },
225 { "rmeta", false, KMOD_RMETA, 0x0A00, 0x0800 },
226 { "num", false, KMOD_NUM, 0x1000, 0x1000 },
227 { "caps", false, KMOD_CAPS, 0x2000, 0x2000 },
228 { "mode", false, KMOD_MODE, 0x4000, 0x4000 },
229 { NULL, false, 0, 0, 0 }
232 struct key
234 const char* name;
235 unsigned symbol;
236 } keys_table[] = {
237 {"backspace", SDLK_BACKSPACE},
238 {"tab", SDLK_TAB},
239 {"clear", SDLK_CLEAR},
240 {"return", SDLK_RETURN},
241 {"pause", SDLK_PAUSE},
242 {"escape", SDLK_ESCAPE},
243 {"space", SDLK_SPACE},
244 {"exclaim", SDLK_EXCLAIM},
245 {"quotedbl", SDLK_QUOTEDBL},
246 {"hash", SDLK_HASH},
247 {"dollar", SDLK_DOLLAR},
248 {"ampersand", SDLK_AMPERSAND},
249 {"quote", SDLK_QUOTE},
250 {"leftparen", SDLK_LEFTPAREN},
251 {"rightparen", SDLK_RIGHTPAREN},
252 {"asterisk", SDLK_ASTERISK},
253 {"plus", SDLK_PLUS},
254 {"comma", SDLK_COMMA},
255 {"minus", SDLK_MINUS},
256 {"period", SDLK_PERIOD},
257 {"slash", SDLK_SLASH},
258 {"0", SDLK_0},
259 {"1", SDLK_1},
260 {"2", SDLK_2},
261 {"3", SDLK_3},
262 {"4", SDLK_4},
263 {"5", SDLK_5},
264 {"6", SDLK_6},
265 {"7", SDLK_7},
266 {"8", SDLK_8},
267 {"9", SDLK_9},
268 {"colon", SDLK_COLON},
269 {"semicolon", SDLK_SEMICOLON},
270 {"less", SDLK_LESS},
271 {"equals", SDLK_EQUALS},
272 {"greater", SDLK_GREATER},
273 {"question", SDLK_QUESTION},
274 {"at", SDLK_AT},
275 {"leftbracket", SDLK_LEFTBRACKET},
276 {"backslash", SDLK_BACKSLASH},
277 {"rightbracket", SDLK_RIGHTBRACKET},
278 {"caret", SDLK_CARET},
279 {"underscore", SDLK_UNDERSCORE},
280 {"backquote", SDLK_BACKQUOTE},
281 {"a", SDLK_a},
282 {"b", SDLK_b},
283 {"c", SDLK_c},
284 {"d", SDLK_d},
285 {"e", SDLK_e},
286 {"f", SDLK_f},
287 {"g", SDLK_g},
288 {"h", SDLK_h},
289 {"i", SDLK_i},
290 {"j", SDLK_j},
291 {"k", SDLK_k},
292 {"l", SDLK_l},
293 {"m", SDLK_m},
294 {"n", SDLK_n},
295 {"o", SDLK_o},
296 {"p", SDLK_p},
297 {"q", SDLK_q},
298 {"r", SDLK_r},
299 {"s", SDLK_s},
300 {"t", SDLK_t},
301 {"u", SDLK_u},
302 {"v", SDLK_v},
303 {"w", SDLK_w},
304 {"x", SDLK_x},
305 {"y", SDLK_y},
306 {"z", SDLK_z},
307 {"delete", SDLK_DELETE},
308 {"world_0", SDLK_WORLD_0},
309 {"world_1", SDLK_WORLD_1},
310 {"world_2", SDLK_WORLD_2},
311 {"world_3", SDLK_WORLD_3},
312 {"world_4", SDLK_WORLD_4},
313 {"world_5", SDLK_WORLD_5},
314 {"world_6", SDLK_WORLD_6},
315 {"world_7", SDLK_WORLD_7},
316 {"world_8", SDLK_WORLD_8},
317 {"world_9", SDLK_WORLD_9},
318 {"world_10", SDLK_WORLD_10},
319 {"world_11", SDLK_WORLD_11},
320 {"world_12", SDLK_WORLD_12},
321 {"world_13", SDLK_WORLD_13},
322 {"world_14", SDLK_WORLD_14},
323 {"world_15", SDLK_WORLD_15},
324 {"world_16", SDLK_WORLD_16},
325 {"world_17", SDLK_WORLD_17},
326 {"world_18", SDLK_WORLD_18},
327 {"world_19", SDLK_WORLD_19},
328 {"world_20", SDLK_WORLD_20},
329 {"world_21", SDLK_WORLD_21},
330 {"world_22", SDLK_WORLD_22},
331 {"world_23", SDLK_WORLD_23},
332 {"world_24", SDLK_WORLD_24},
333 {"world_25", SDLK_WORLD_25},
334 {"world_26", SDLK_WORLD_26},
335 {"world_27", SDLK_WORLD_27},
336 {"world_28", SDLK_WORLD_28},
337 {"world_29", SDLK_WORLD_29},
338 {"world_30", SDLK_WORLD_30},
339 {"world_31", SDLK_WORLD_31},
340 {"world_32", SDLK_WORLD_32},
341 {"world_33", SDLK_WORLD_33},
342 {"world_34", SDLK_WORLD_34},
343 {"world_35", SDLK_WORLD_35},
344 {"world_36", SDLK_WORLD_36},
345 {"world_37", SDLK_WORLD_37},
346 {"world_38", SDLK_WORLD_38},
347 {"world_39", SDLK_WORLD_39},
348 {"world_40", SDLK_WORLD_40},
349 {"world_41", SDLK_WORLD_41},
350 {"world_42", SDLK_WORLD_42},
351 {"world_43", SDLK_WORLD_43},
352 {"world_44", SDLK_WORLD_44},
353 {"world_45", SDLK_WORLD_45},
354 {"world_46", SDLK_WORLD_46},
355 {"world_47", SDLK_WORLD_47},
356 {"world_48", SDLK_WORLD_48},
357 {"world_49", SDLK_WORLD_49},
358 {"world_50", SDLK_WORLD_50},
359 {"world_51", SDLK_WORLD_51},
360 {"world_52", SDLK_WORLD_52},
361 {"world_53", SDLK_WORLD_53},
362 {"world_54", SDLK_WORLD_54},
363 {"world_55", SDLK_WORLD_55},
364 {"world_56", SDLK_WORLD_56},
365 {"world_57", SDLK_WORLD_57},
366 {"world_58", SDLK_WORLD_58},
367 {"world_59", SDLK_WORLD_59},
368 {"world_60", SDLK_WORLD_60},
369 {"world_61", SDLK_WORLD_61},
370 {"world_62", SDLK_WORLD_62},
371 {"world_63", SDLK_WORLD_63},
372 {"world_64", SDLK_WORLD_64},
373 {"world_65", SDLK_WORLD_65},
374 {"world_66", SDLK_WORLD_66},
375 {"world_67", SDLK_WORLD_67},
376 {"world_68", SDLK_WORLD_68},
377 {"world_69", SDLK_WORLD_69},
378 {"world_70", SDLK_WORLD_70},
379 {"world_71", SDLK_WORLD_71},
380 {"world_72", SDLK_WORLD_72},
381 {"world_73", SDLK_WORLD_73},
382 {"world_74", SDLK_WORLD_74},
383 {"world_75", SDLK_WORLD_75},
384 {"world_76", SDLK_WORLD_76},
385 {"world_77", SDLK_WORLD_77},
386 {"world_78", SDLK_WORLD_78},
387 {"world_79", SDLK_WORLD_79},
388 {"world_80", SDLK_WORLD_80},
389 {"world_81", SDLK_WORLD_81},
390 {"world_82", SDLK_WORLD_82},
391 {"world_83", SDLK_WORLD_83},
392 {"world_84", SDLK_WORLD_84},
393 {"world_85", SDLK_WORLD_85},
394 {"world_86", SDLK_WORLD_86},
395 {"world_87", SDLK_WORLD_87},
396 {"world_88", SDLK_WORLD_88},
397 {"world_89", SDLK_WORLD_89},
398 {"world_90", SDLK_WORLD_90},
399 {"world_91", SDLK_WORLD_91},
400 {"world_92", SDLK_WORLD_92},
401 {"world_93", SDLK_WORLD_93},
402 {"world_94", SDLK_WORLD_94},
403 {"world_95", SDLK_WORLD_95},
404 {"kp0", SDLK_KP0},
405 {"kp1", SDLK_KP1},
406 {"kp2", SDLK_KP2},
407 {"kp3", SDLK_KP3},
408 {"kp4", SDLK_KP4},
409 {"kp5", SDLK_KP5},
410 {"kp6", SDLK_KP6},
411 {"kp7", SDLK_KP7},
412 {"kp8", SDLK_KP8},
413 {"kp9", SDLK_KP9},
414 {"kp_period", SDLK_KP_PERIOD},
415 {"kp_divide", SDLK_KP_DIVIDE},
416 {"kp_multiply", SDLK_KP_MULTIPLY},
417 {"kp_minus", SDLK_KP_MINUS},
418 {"kp_plus", SDLK_KP_PLUS},
419 {"kp_enter", SDLK_KP_ENTER},
420 {"kp_equals", SDLK_KP_EQUALS},
421 {"up", SDLK_UP},
422 {"down", SDLK_DOWN},
423 {"right", SDLK_RIGHT},
424 {"left", SDLK_LEFT},
425 {"insert", SDLK_INSERT},
426 {"home", SDLK_HOME},
427 {"end", SDLK_END},
428 {"pageup", SDLK_PAGEUP},
429 {"pagedown", SDLK_PAGEDOWN},
430 {"f1", SDLK_F1},
431 {"f2", SDLK_F2},
432 {"f3", SDLK_F3},
433 {"f4", SDLK_F4},
434 {"f5", SDLK_F5},
435 {"f6", SDLK_F6},
436 {"f7", SDLK_F7},
437 {"f8", SDLK_F8},
438 {"f9", SDLK_F9},
439 {"f10", SDLK_F10},
440 {"f11", SDLK_F11},
441 {"f12", SDLK_F12},
442 {"f13", SDLK_F13},
443 {"f14", SDLK_F14},
444 {"f15", SDLK_F15},
445 {"numlock", SDLK_NUMLOCK},
446 {"capslock", SDLK_CAPSLOCK},
447 {"scrollock", SDLK_SCROLLOCK},
448 {"rshift", SDLK_RSHIFT},
449 {"lshift", SDLK_LSHIFT},
450 {"rctrl", SDLK_RCTRL},
451 {"lctrl", SDLK_LCTRL},
452 {"ralt", SDLK_RALT},
453 {"lalt", SDLK_LALT},
454 {"rmeta", SDLK_RMETA},
455 {"lmeta", SDLK_LMETA},
456 {"lsuper", SDLK_LSUPER},
457 {"rsuper", SDLK_RSUPER},
458 {"mode", SDLK_MODE},
459 {"compose", SDLK_COMPOSE},
460 {"help", SDLK_HELP},
461 {"print", SDLK_PRINT},
462 {"sysreq", SDLK_SYSREQ},
463 {"break", SDLK_BREAK},
464 {"menu", SDLK_MENU},
465 {"power", SDLK_POWER},
466 {"euro", SDLK_EURO},
467 {"undo", SDLK_UNDO},
468 {NULL, 0}
471 unsigned recognize_one_modifier(std::string mod)
473 struct modifier* m = modifiers_table;
474 while(m->name) {
475 if(mod == std::string(m->name))
476 return m->asmodifier;
477 m++;
479 throw std::runtime_error("Unknown modifier '" + mod + "'");
482 uint32_t transpack[16] = {0, 1, 3, 2, 5, 4, 0, 7, 8, 0, 0, 6, 0, 0, 0};
483 uint32_t polarities[9] = {1, 1, 1, 1, 0, 0, 0, 0, 0};
484 uint32_t directions[9] = {1, 2, 4, 8, 1, 2, 4, 8, 0};
486 uint64_t X[] = {
487 0x032221008ULL, //From center.
488 0x344444184ULL, //From up.
489 0x555544855ULL, //From up&right.
490 0x555528055ULL, //From right.
491 0x555586655ULL, //From down&right.
492 0x663816666ULL, //From down.
493 0x668777777ULL, //From down&left.
494 0x082777777ULL, //From left.
495 0x844777777ULL //From up&left.
498 void hat_transition(uint32_t& ov, uint32_t& v, bool& polarity, bool& more)
500 uint32_t t1 = transpack[ov];
501 uint32_t t2 = transpack[v];
502 more = (v != ((X[t1] >> 4 * t2) & 0xF));
503 polarity = polarities[(X[t1] >> 4 * t2) & 0xF];
504 ov ^= directions[(X[t1] >> 4 * t2) & 0xF];
505 v = directions[(X[t1] >> 4 * t2) & 0xF];
509 keymapper_helper_sdl::translated_event::translated_event(keysymbol _symbol, bool _polarity, bool _more)
511 symbol = _symbol;
512 polarity = _polarity;
513 more = _more;
516 keymapper_helper_sdl::translated_event keymapper_helper_sdl::translate_event(SDL_Event& e)
517 throw(std::bad_alloc, std::runtime_error)
519 if(e.type == SDL_JOYHATMOTION) {
520 uint32_t axis = static_cast<uint32_t>(e.jhat.which) * 256 + static_cast<uint32_t>(e.jhat.hat);
521 uint32_t v = 0;
522 if(e.jhat.value & SDL_HAT_UP) v |= 1;
523 if(e.jhat.value & SDL_HAT_RIGHT) v |= 2;
524 if(e.jhat.value & SDL_HAT_DOWN) v |= 4;
525 if(e.jhat.value & SDL_HAT_LEFT) v |= 8;
526 unsigned ov = hat_current_state[axis];
527 bool more = false;
528 bool polarity = false;
529 if(ov == v) {
530 keysymbol k;
531 k.device = SDL_DEV_NONE;
532 return translated_event(k, false);
534 hat_transition(ov, v, polarity, more);
535 hat_current_state[axis] = ov;
536 axis |= (v << 16);
537 keysymbol k;
538 k.device = SDL_DEV_JOYHAT;
539 k.joyhat = axis;
540 return translated_event(k, polarity, more);
541 } else if(e.type == SDL_JOYAXISMOTION) {
542 uint32_t axis = static_cast<uint32_t>(e.jaxis.which) * 256 + static_cast<uint32_t>(e.jaxis.axis);
543 int v = calibrations[axis].digital(e.jaxis.value);
544 int ov = axis_current_state[axis];
545 bool more = false;
546 bool polarity = false;
547 if(ov == v) {
548 keysymbol k;
549 k.device = SDL_DEV_NONE;
550 return translated_event(k, false);
551 } else if(ov == -1) {
552 more = (v == 1);
553 polarity = false;
554 } else if(ov == 0) {
555 polarity = true;
556 axis |= ((v == 1) ? 0x10000 : 0);
557 } else if(ov == 1) {
558 more = (v == -1);
559 polarity = false;
560 axis |= 0x10000;
562 keysymbol k;
563 k.device = SDL_DEV_JOYAXIS;
564 k.joyaxis = axis;
565 return translated_event(k, polarity, more);
566 } else if(e.type == SDL_JOYBUTTONUP || e.type == SDL_JOYBUTTONDOWN) {
567 keysymbol k;
568 k.device = SDL_DEV_JOYBUTTON;
569 k.joybutton = static_cast<uint32_t>(e.jbutton.which) * 256 + static_cast<uint32_t>(e.jbutton.button);
570 return translated_event(k, (e.type == SDL_JOYBUTTONDOWN));
572 } else if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP) {
573 keysymbol k;
574 k.device = SDL_DEV_KEYBOARD;
575 k.keyboard = e.key.keysym;
576 return translated_event(k, (e.type == SDL_KEYDOWN));
577 } else {
578 keysymbol k;
579 k.device = SDL_DEV_NONE;
580 return translated_event(k, false);
584 bool keymapper_helper_sdl::internal_keysymbol::operator==(const internal_keysymbol& k) const
586 if(mode == KEYTYPE_SCAN_SYMBOL && k.mode == KEYTYPE_SCAN_SYMBOL)
587 return (scan == k.scan || symbolic == k.symbolic);
588 if((mode == KEYTYPE_SCAN_SYMBOL && k.mode == KEYTYPE_SCAN) || (k.mode == KEYTYPE_SCAN_SYMBOL &&
589 mode == KEYTYPE_SCAN) || (k.mode == KEYTYPE_SCAN && mode == KEYTYPE_SCAN))
590 return (scan == k.scan);
591 if((mode == KEYTYPE_SCAN_SYMBOL && k.mode == KEYTYPE_SYMBOL) || (k.mode == KEYTYPE_SCAN_SYMBOL &&
592 mode == KEYTYPE_SYMBOL) || (k.mode == KEYTYPE_SYMBOL && mode == KEYTYPE_SYMBOL))
593 return (symbolic == k.symbolic);
594 if(mode == KEYTYPE_JOYAXIS && k.mode == KEYTYPE_JOYAXIS)
595 return (joyaxis == k.joyaxis);
596 if(mode == KEYTYPE_JOYHAT && k.mode == KEYTYPE_JOYHAT) {
597 //Joystick hats are handled specially. Diffrent hats never compare equal.
598 if((joyhat & 0xFFFF) != (k.joyhat & 0xFFFF))
599 return false;
600 //For the same hat, if there is overlap, then they compare equal.
601 return (((joyhat & k.joyhat) >> 16) != 0);
603 return false;
606 unsigned keymapper_helper_sdl::mod_str(std::string mod) throw(std::bad_alloc, std::runtime_error)
608 unsigned ret = 0;
609 while(mod != "") {
610 size_t split = mod.find_first_of(",");
611 std::string omod;
612 if(split < mod.length()) {
613 omod = mod.substr(0, split);
614 mod = mod.substr(split + 1);
615 if(mod == "")
616 throw std::runtime_error("Invalid modifier specification (trailing comma)");
617 } else {
618 omod = mod;
619 mod = "";
621 ret |= recognize_one_modifier(omod);
623 return ret;
626 unsigned keymapper_helper_sdl::mod_key(keymapper_helper_sdl::keysymbol key) throw(std::bad_alloc)
628 if(key.device != SDL_DEV_KEYBOARD)
629 return 0;
630 struct modifier* m = modifiers_table;
631 unsigned ret = 0;
632 while(m->name) {
633 if(!m->synthethic && (key.keyboard.mod & m->sdlvalue))
634 ret |= m->asrealkey;
635 m++;
637 return ret;
640 keymapper_helper_sdl::internal_keysymbol keymapper_helper_sdl::key_str(std::string key) throw(std::bad_alloc,
641 std::runtime_error)
643 if(key.length() > 8 && key.substr(0, 8) == "joystick") {
644 const char* rest = key.c_str() + 8;
645 char* end;
646 unsigned subtype = 0;
647 unsigned x;
648 unsigned long value = strtoul(rest, &end, 10);
649 if(value > 255)
650 throw std::runtime_error("Bad joystick number '" + key + "'");
651 x = value * 256;
652 if(!strncmp(end, "button", 6)) {
653 subtype = 1;
654 rest = end + 6;
655 } else if(!strncmp(end, "axis", 4)) {
656 subtype = 2;
657 rest = end + 4;
658 } else if(!strncmp(end, "hat", 3)) {
659 subtype = 3;
660 rest = end + 3;
661 } else
662 throw std::runtime_error("Bad joystick subtype '" + key + "'");
663 value = strtoul(rest, &end, 10);
664 x += value;
665 if(subtype == 1) {
666 if(value > 255 || *end)
667 throw std::runtime_error("Bad joystick button '" + key + "'");
668 internal_keysymbol k;
669 k.mode = KEYTYPE_JOYBUTTON;
670 k.joybutton = x;
671 return k;
672 } else if(subtype == 2) {
673 if(!strncmp(end, "+", 1) && value <= 255)
674 x |= 0x10000;
675 else if(!strncmp(end, "-", 1) && value <= 255)
677 else
678 throw std::runtime_error("Bad joystick axis '" + key + "'");
679 internal_keysymbol k;
680 k.mode = KEYTYPE_JOYAXIS;
681 k.joyaxis = x;
682 return k;
683 } else if(subtype == 3) {
684 if(!strncmp(end, "n", 1) && value <= 255)
685 x |= 0x10000;
686 else if(!strncmp(end, "e", 1) && value <= 255)
687 x |= 0x20000;
688 else if(!strncmp(end, "s", 1) && value <= 255)
689 x |= 0x40000;
690 else if(!strncmp(end, "w", 1) && value <= 255)
691 x |= 0x80000;
692 else
693 throw std::runtime_error("Bad joystick hat '" + key + "'");
694 internal_keysymbol k;
695 k.mode = KEYTYPE_JOYHAT;
696 k.joyhat = x;
697 return k;
699 } if(key.length() > 3 && key.substr(0, 3) == "key") {
700 const char* rest = key.c_str() + 3;
701 char* end;
702 unsigned long value = strtoul(rest, &end, 10);
703 if(*end || value > 255)
704 throw std::runtime_error("Bad scancode keysymbol '" + key + "'");
705 internal_keysymbol k;
706 k.mode = KEYTYPE_SCAN;
707 k.scan = value;
708 k.symbolic = 0;
709 return k;
711 struct key* k = keys_table;
712 while(k->name) {
713 if(key == std::string(k->name)) {
714 internal_keysymbol k2;
715 k2.mode = KEYTYPE_SYMBOL;
716 k2.scan = 0;
717 k2.symbolic = k->symbol;
718 return k2;
720 k++;
722 throw std::runtime_error("Unknown key '" + key + "'");
725 keymapper_helper_sdl::internal_keysymbol keymapper_helper_sdl::key_key(keymapper_helper_sdl::keysymbol key)
726 throw(std::bad_alloc)
728 internal_keysymbol k;
729 if(key.device == SDL_DEV_KEYBOARD) {
730 k.mode = KEYTYPE_SCAN_SYMBOL;
731 k.scan = key.keyboard.scancode;
732 k.symbolic = key.keyboard.sym;
733 } else if(key.device == SDL_DEV_JOYAXIS) {
734 k.mode = KEYTYPE_JOYAXIS;
735 k.joyaxis = key.joyaxis;
736 } else if(key.device == SDL_DEV_JOYBUTTON) {
737 k.mode = KEYTYPE_JOYBUTTON;
738 k.joybutton = key.joybutton;
739 } else if(key.device == SDL_DEV_JOYHAT) {
740 k.mode = KEYTYPE_JOYHAT;
741 k.joyhat = key.joyhat;
742 } else {
743 k.mode = KEYTYPE_NONE;
745 return k;
748 std::string keymapper_helper_sdl::name_key(unsigned mod, unsigned modmask, struct internal_keysymbol key)
749 throw(std::bad_alloc)
751 std::string ret = "";
752 if(mod != 0 || modmask != 0) {
753 struct modifier* m = modifiers_table;
754 while(m->name) {
755 if((mod & m->asmodifier) == m->asmodifier) {
756 ret = ret + m->name + ",";
758 m++;
760 ret = ret.substr(0, ret.length() - 1);
761 ret = ret + "/";
762 m = modifiers_table;
763 while(m->name) {
764 if((modmask & m->asmodifier) == m->asmodifier) {
765 ret = ret + m->name + ",";
767 m++;
769 ret = ret.substr(0, ret.length() - 1);
770 ret = ret + " ";
772 if(key.mode == KEYTYPE_SCAN_SYMBOL || key.mode == KEYTYPE_SYMBOL) {
773 struct key* k = keys_table;
774 while(k->name) {
775 if(key.symbolic == k->symbol) {
776 ret = ret + k->name;
777 break;
779 k++;
781 } else if(key.mode == KEYTYPE_SCAN) {
782 std::ostringstream x;
783 x << key.scan;
784 ret = ret + "key" + x.str();
785 } else if(key.mode == KEYTYPE_JOYAXIS) {
786 std::ostringstream x;
787 x << "joystick" << ((key.joyaxis >> 8) & 0xFF) << "axis" << (key.joyaxis & 0xFF);
788 x << ((key.joyaxis & 0x10000) ? '+' : '-');
789 ret = ret + x.str();
790 } else if(key.mode == KEYTYPE_JOYBUTTON) {
791 std::ostringstream x;
792 x << "joystick" << ((key.joybutton >> 8) & 0xFF) << "button" << (key.joybutton & 0xFF);
793 ret = ret + x.str();
794 } else if(key.mode == KEYTYPE_JOYHAT) {
795 std::ostringstream x;
796 x << "joystick" << ((key.joyhat >> 8) & 0xFF) << "hat" << (key.joyhat & 0xFF);
797 if(key.joyhat & 0x10000)
798 x << "n";
799 if(key.joyhat & 0x40000)
800 x << "s";
801 if(key.joyhat & 0x20000)
802 x << "e";
803 if(key.joyhat & 0x80000)
804 x << "w";
805 ret = ret + x.str();
806 } else {
807 ret = ret + "<invalid>";
809 return ret;
812 std::string keymapper_helper_sdl::print_key_info(keymapper_helper_sdl::keysymbol key) throw(std::bad_alloc)
814 std::string ret;
815 if(key.device == SDL_DEV_KEYBOARD && key.keyboard.mod) {
816 ret = ret + "Modifiers: ";
817 unsigned mod = mod_key(key);
818 struct modifier* m = modifiers_table;
819 while(m->name) {
820 if((mod & m->asmodifier) == m->asmodifier) {
821 ret = ret + m->name + " ";
823 m++;
825 ret = ret + "\n";
827 if(key.device == SDL_DEV_KEYBOARD) {
829 std::ostringstream x;
830 x << static_cast<unsigned>(key.keyboard.scancode);
831 ret = ret + "Name: key" + x.str() + "\n";
833 struct key* k = keys_table;
834 while(k->name) {
835 if(key.keyboard.sym == k->symbol) {
836 ret = ret + "Name: " + k->name + "\n";
837 break;
839 k++;
841 } else if(key.device == SDL_DEV_JOYAXIS) {
842 std::ostringstream x;
843 x << "joystick" << ((key.joyaxis >> 8) & 0xFF) << "axis" << (key.joyaxis & 0xFF);
844 x << ((key.joyaxis & 0x10000) ? '+' : '-');
845 ret + ret + "Name: " + x.str() + "\n";
846 } else if(key.device == SDL_DEV_JOYBUTTON) {
847 std::ostringstream x;
848 x << "joystick" << ((key.joybutton >> 8) & 0xFF) << "button" << (key.joybutton & 0xFF);
849 ret + ret + "Name: " + x.str() + "\n";
850 } else if(key.device == SDL_DEV_JOYHAT) {
851 std::ostringstream x;
852 x << "joystick" << ((key.joyhat >> 8) & 0xFF) << "hat" << (key.joyhat & 0xFF);
853 if(key.joyhat & 0x10000)
854 x << "n";
855 if(key.joyhat & 0x20000)
856 x << "e";
857 if(key.joyhat & 0x40000)
858 x << "s";
859 if(key.joyhat & 0x80000)
860 x << "w";
861 ret + ret + "Name: " + x.str() + "\n";
863 return ret;
867 extern uint32_t fontdata[];
870 namespace
872 uint32_t mouse_mask = 0;
873 uint32_t vc_xoffset;
874 uint32_t vc_yoffset;
875 uint32_t vc_hscl = 1;
876 uint32_t vc_vscl = 1;
877 bool sdl_init;
878 window* win;
879 bool modconfirm;
880 bool modal_return_flag;
881 bool delayed_close_flag;
882 std::string modmsg;
883 std::string command_buf;
884 bool command_overwrite;
885 size_t command_cursor;
886 unsigned old_screen_w;
887 unsigned old_screen_h;
888 unsigned state;
889 std::map<std::string, std::string> emustatus;
890 std::map<uint64_t, std::string> messagebuffer;
891 uint64_t messagebuffer_next_seq;
892 uint64_t messagebuffer_first_seq;
893 uint64_t messagebuffer_first_show;
894 bool console_mode;
895 uint32_t maxmessages;
896 std::list<std::string> commandhistory;
897 std::list<std::string>::iterator commandhistory_itr;
898 screen* current_screen;
899 keymapper<keymapper_helper_sdl> mapper;
900 SDL_Surface* hwsurf;
901 bool pause_active;
902 uint64_t last_ui_update;
903 bool screen_is_dirty;
904 std::ofstream system_log;
905 SDL_keysym autorepeating_key;
906 unsigned autorepeat_phase = 0;
907 unsigned autorepeat_timecounter = 0;
908 numeric_setting autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
909 numeric_setting autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
913 namespace
915 const size_t audiobuf_size = 8192;
916 uint16_t audiobuf[audiobuf_size];
917 volatile size_t audiobuf_get = 0;
918 volatile size_t audiobuf_put = 0;
919 uint64_t sampledup_ctr = 0;
920 uint64_t sampledup_inc = 0;
921 uint64_t sampledup_mod = 1;
922 Uint16 format = AUDIO_S16SYS;
923 bool stereo = true;
924 bool sound_enabled = true;
926 void calculate_sampledup(uint32_t real_rate)
928 sampledup_ctr = 0;
929 sampledup_inc = 64081;
930 sampledup_mod = 2 * real_rate + 64081;
933 void audiocb(void* dummy, Uint8* stream, int len)
935 static uint16_t lprev = 32768;
936 static uint16_t rprev = 32768;
937 if(!sound_enabled)
938 lprev = rprev = 32768;
939 uint16_t bias = (format == AUDIO_S8 || format == AUDIO_S16LSB || format == AUDIO_S16MSB || format ==
940 AUDIO_S16SYS) ? 32768 : 0;
941 while(len > 0) {
942 uint16_t l, r;
943 if(audiobuf_get == audiobuf_put) {
944 l = lprev;
945 r = rprev;
946 } else {
947 l = lprev = audiobuf[audiobuf_get++];
948 r = rprev = audiobuf[audiobuf_get++];
949 if(audiobuf_get == audiobuf_size)
950 audiobuf_get = 0;
952 if(!stereo)
953 l = l / 2 + r / 2;
954 if(format == AUDIO_U8 || format == AUDIO_S8) {
955 stream[0] = (l - bias) >> 8;
956 if(stereo)
957 stream[1] = (r - bias) >> 8;
958 stream += (stereo ? 2 : 1);
959 len -= (stereo ? 2 : 1);
960 } else if(format == AUDIO_S16SYS || format == AUDIO_U16SYS) {
961 reinterpret_cast<uint16_t*>(stream)[0] = (l - bias);
962 if(stereo)
963 reinterpret_cast<int16_t*>(stream)[1] = (r - bias);
964 stream += (stereo ? 4 : 2);
965 len -= (stereo ? 4 : 2);
966 } else if(format == AUDIO_S16LSB || format == AUDIO_U16LSB) {
967 stream[0] = (l - bias);
968 stream[1] = (l - bias) >> 8;
969 if(stereo) {
970 stream[2] = (r - bias);
971 stream[3] = (r - bias) >> 8;
973 stream += (stereo ? 4 : 2);
974 len -= (stereo ? 4 : 2);
975 } else if(format == AUDIO_S16MSB || format == AUDIO_U16MSB) {
976 stream[1] = (l - bias);
977 stream[0] = (l - bias) >> 8;
978 if(stereo) {
979 stream[3] = (r - bias);
980 stream[2] = (r - bias) >> 8;
982 stream += (stereo ? 4 : 2);
983 len -= (stereo ? 4 : 2);
988 void identify()
990 state = WINSTATE_IDENTIFY;
991 win->message("Press key to identify.");
992 win->notify_screen_update();
993 win->poll_inputs();
996 std::string decode_string(std::string e)
998 std::string x;
999 for(size_t i = 0; i < e.length(); i += 4) {
1000 char tmp[5] = {0};
1001 uint32_t c1 = e[i] - 33;
1002 uint32_t c2 = e[i + 1] - 33;
1003 uint32_t c3 = e[i + 2] - 33;
1004 uint32_t c4 = e[i + 3] - 33;
1005 uint32_t c = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
1006 if(c < 0x80) {
1007 tmp[0] = c;
1008 } else if(c < 0x800) {
1009 tmp[0] = 0xC0 | (c >> 6);
1010 tmp[1] = 0x80 | (c & 0x3F);
1011 } else if(c < 0x10000) {
1012 tmp[0] = 0xE0 | (c >> 12);
1013 tmp[1] = 0x80 | ((c >> 6) & 0x3F);
1014 tmp[2] = 0x80 | (c & 0x3F);
1015 } else {
1016 tmp[0] = 0xF0 | (c >> 18);
1017 tmp[1] = 0x80 | ((c >> 12) & 0x3F);
1018 tmp[2] = 0x80 | ((c >> 6) & 0x3F);
1019 tmp[3] = 0x80 | (c & 0x3F);
1021 x = x + tmp;
1023 return x;
1026 void draw_rectangle(uint8_t* data, uint32_t pitch, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1027 uint32_t color, uint32_t thickness)
1029 for(uint32_t i = x1; i < x2; i++)
1030 for(uint32_t j = 0; j < thickness; j++) {
1031 reinterpret_cast<uint32_t*>(data + pitch * (y1 + j))[i] = color;
1032 reinterpret_cast<uint32_t*>(data + pitch * (y2 - 1 - j))[i] = color;
1034 for(uint32_t i = y1; i < y2; i++)
1035 for(uint32_t j = 0; j < thickness; j++) {
1036 reinterpret_cast<uint32_t*>(data + pitch * i)[x1 + j] = color;
1037 reinterpret_cast<uint32_t*>(data + pitch * i)[x2 - 1 - j] = color;
1041 std::vector<uint32_t> decode_utf8(std::string s)
1043 std::vector<uint32_t> ret;
1044 for(auto i = s.begin(); i != s.end(); i++) {
1045 uint32_t j = static_cast<uint8_t>(*i);
1046 if(j < 128)
1047 ret.push_back(j);
1048 else if(j < 192)
1049 continue;
1050 else if(j < 224) {
1051 uint32_t j2 = static_cast<uint8_t>(*(++i));
1052 ret.push_back((j - 192) * 64 + (j2 - 128));
1053 } else if(j < 240) {
1054 uint32_t j2 = static_cast<uint8_t>(*(++i));
1055 uint32_t j3 = static_cast<uint8_t>(*(++i));
1056 ret.push_back((j - 224) * 4096 + (j2 - 128) * 64 + (j3 - 128));
1057 } else {
1058 uint32_t j2 = static_cast<uint8_t>(*(++i));
1059 uint32_t j3 = static_cast<uint8_t>(*(++i));
1060 uint32_t j4 = static_cast<uint8_t>(*(++i));
1061 ret.push_back((j - 240) * 262144 + (j2 - 128) * 4096 + (j3 - 128) * 64 + (j4 - 128));
1064 return ret;
1067 void draw_string(uint8_t* base, uint32_t pitch, std::vector<uint32_t> s, uint32_t x, uint32_t y,
1068 uint32_t maxwidth, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
1070 base += y * static_cast<size_t>(pitch) + 4 * x;
1071 int32_t pos_x = 0;
1072 int32_t pos_y = 0;
1073 unsigned c = 0;
1074 for(auto si = s.begin(); si != s.end(); si++) {
1075 uint32_t old_x = pos_x;
1076 uint32_t curstart = 16;
1077 if(c == hilite_pos && hilite_mode == 1)
1078 curstart = 14;
1079 if(c == hilite_pos && hilite_mode == 2)
1080 curstart = 0;
1081 auto g = find_glyph(*si, pos_x, pos_y, 0, pos_x, pos_y);
1082 if(pos_y)
1083 pos_x = old_x;
1084 if(g.second == 0) {
1085 //Empty glyph.
1086 for(unsigned j = 0; j < 16; j++) {
1087 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
1088 for(unsigned i = 0; i < g.first && old_x + i < maxwidth; i++)
1089 ptr[old_x + i] = (j >= curstart) ? 0xFFFFFFFFU : 0;
1091 } else {
1092 //Narrow/Wide glyph.
1093 for(unsigned j = 0; j < 16; j++) {
1094 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
1095 uint32_t dataword = fontdata[g.second + j / 4];
1096 for(uint32_t i = 0; i < g.first && old_x + i < maxwidth; i++) {
1097 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
1098 1));
1099 b ^= (j >= curstart);
1100 ptr[old_x + i] = b ? 0xFFFFFFFFU : 0;
1104 c++;
1106 for(unsigned j = 0; j < 16; j++) {
1107 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + pitch * j);
1108 uint32_t curstart = 16;
1109 if(c == hilite_pos && hilite_mode == 1)
1110 curstart = 14;
1111 if(c == hilite_pos && hilite_mode == 2)
1112 curstart = 0;
1113 for(uint32_t i = pos_x; i < maxwidth; i++) {
1114 ptr[i] = ((i - pos_x) < 8 && j >= curstart) ? 0xFFFFFFFFU : 0;
1119 void draw_string(uint8_t* base, uint32_t pitch, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth,
1120 uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
1122 draw_string(base, pitch, decode_utf8(s), x, y, maxwidth, hilite_mode, hilite_pos);
1125 void draw_command(uint8_t* base, uint32_t pitch, std::string s, size_t cursor, uint32_t x, uint32_t y,
1126 uint32_t maxwidth, bool overwrite)
1128 //FIXME, scroll text if too long.
1129 uint32_t hilite_mode = overwrite ? 2 : 1;
1130 auto s2 = decode_utf8(s);
1131 draw_string(base, pitch, s2, x, y, maxwidth, hilite_mode, cursor);
1134 void draw_modal_dialog(SDL_Surface* surf, std::string msg, bool confirm)
1136 int32_t pos_x = 0;
1137 int32_t pos_y = 0;
1138 uint32_t width = 0;
1139 uint32_t height = 0;
1140 if(confirm)
1141 msg = msg + "\n\nHit Enter to confirm, Esc to cancel";
1142 else
1143 msg = msg + "\n\nHit Enter or Esc to dismiss";
1144 auto s2 = decode_utf8(msg);
1145 for(auto i = s2.begin(); i != s2.end(); i++) {
1146 auto g = find_glyph(*i, pos_x, pos_y, 0, pos_x, pos_y);
1147 if(pos_x + g.first > width)
1148 width = static_cast<uint32_t>(pos_x + g.first);
1149 if(pos_y + 16 > static_cast<int32_t>(height))
1150 height = static_cast<uint32_t>(pos_y + 16);
1152 uint32_t x1;
1153 uint32_t x2;
1154 uint32_t y1;
1155 uint32_t y2;
1156 if(width + 12 >= static_cast<uint32_t>(surf->w)) {
1157 x1 = 6;
1158 x2 = surf->w - 6;
1159 width = x2 - x1;
1160 } else {
1161 x1 = (surf->w - width) / 2;
1162 x2 = x1 + width;
1164 if(height + 12 >= static_cast<uint32_t>(surf->h)) {
1165 y1 = 6;
1166 y2 = surf->h - 6;
1167 height = y2 - y1;
1168 } else {
1169 y1 = (surf->h - height) / 2;
1170 y2 = y1 + height;
1172 SDL_LockSurface(surf);
1173 for(uint32_t j = y1 - 6; j < y2 + 6; j++)
1174 memset(reinterpret_cast<uint8_t*>(surf->pixels) + j * surf->pitch + 4 * (x1 - 6), 0,
1175 4 * (x2 - x1 + 12));
1176 uint32_t bordercolor = (128 << surf->format->Gshift) | (255 << surf->format->Rshift);
1177 draw_rectangle(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, x1 - 4, y1 - 4, x2 + 4, y2 + 4,
1178 bordercolor, 2);
1180 pos_x = 0;
1181 pos_y = 0;
1182 for(auto i = s2.begin(); i != s2.end(); i++) {
1183 uint32_t ox = pos_x;
1184 uint32_t oy = pos_y;
1185 auto g = find_glyph(*i, pos_x, pos_y, 0, pos_x, pos_y);
1186 if(static_cast<uint32_t>(pos_y) > height)
1187 break;
1188 uint8_t* base = reinterpret_cast<uint8_t*>(surf->pixels) + (y1 + oy) * surf->pitch +
1189 4 * (x1 + ox);
1190 if(g.second) {
1191 //Narrow/Wide glyph.
1192 for(unsigned j = 0; j < 16; j++) {
1193 uint32_t* ptr = reinterpret_cast<uint32_t*>(base + surf->pitch * j);
1194 uint32_t dataword = fontdata[g.second + j / 4];
1195 for(uint32_t i = 0; i < g.first && (ox + i) < width; i++) {
1196 bool b = (((dataword >> (31 - (j % (32 / g.first)) * g.first - i)) &
1197 1));
1198 ptr[i] = b ? bordercolor : 0;
1206 void do_keyboard_command_edit(SDL_keysym k)
1208 //These are not command edit!
1209 if(k.sym == SDLK_ESCAPE)
1210 return;
1211 if(k.sym == SDLK_RETURN)
1212 return;
1213 if(k.sym == SDLK_KP_ENTER)
1214 return;
1215 //Map keys a bit if numlock is off.
1216 if((k.mod & KMOD_NUM) == 0) {
1217 switch(k.sym) {
1218 case SDLK_KP0: k.sym = SDLK_INSERT; break;
1219 case SDLK_KP1: k.sym = SDLK_END; break;
1220 case SDLK_KP2: k.sym = SDLK_DOWN; break;
1221 case SDLK_KP3: k.sym = SDLK_PAGEDOWN; break;
1222 case SDLK_KP4: k.sym = SDLK_LEFT; break;
1223 case SDLK_KP5: return;
1224 case SDLK_KP6: k.sym = SDLK_RIGHT; break;
1225 case SDLK_KP7: k.sym = SDLK_HOME; break;
1226 case SDLK_KP8: k.sym = SDLK_UP; break;
1227 case SDLK_KP9: k.sym = SDLK_PAGEUP; break;
1228 case SDLK_KP_PERIOD: k.sym = SDLK_DELETE; break;
1229 default:
1230 break;
1233 //Special editing operations.
1234 switch(k.sym) {
1235 case SDLK_INSERT:
1236 command_overwrite = !command_overwrite;
1237 win->notify_screen_update();
1238 return;
1239 case SDLK_END:
1240 command_cursor = command_buf.length();
1241 win->notify_screen_update();
1242 return;
1243 case SDLK_DOWN:
1244 case SDLK_PAGEDOWN:
1245 if(commandhistory_itr != commandhistory.begin()) {
1246 commandhistory_itr--;
1247 command_buf = *commandhistory_itr;
1248 if(command_cursor > command_buf.length())
1249 command_cursor = command_buf.length();
1251 win->notify_screen_update();
1252 return;
1253 case SDLK_LEFT:
1254 command_cursor = (command_cursor > 0) ? (command_cursor - 4) : 0;
1255 win->notify_screen_update();
1256 return;
1257 case SDLK_RIGHT:
1258 command_cursor = (command_cursor < command_buf.length()) ? (command_cursor + 4) :
1259 command_buf.length();
1260 win->notify_screen_update();
1261 return;
1262 case SDLK_HOME:
1263 command_cursor = 0;
1264 win->notify_screen_update();
1265 return;
1266 case SDLK_UP:
1267 case SDLK_PAGEUP: {
1268 auto tmp = commandhistory_itr;
1269 if(++tmp != commandhistory.end()) {
1270 commandhistory_itr++;
1271 command_buf = *commandhistory_itr;
1272 if(command_cursor > command_buf.length())
1273 command_cursor = command_buf.length();
1275 win->notify_screen_update();
1276 return;
1278 case SDLK_DELETE:
1279 if(command_cursor < command_buf.length())
1280 command_buf = command_buf.substr(0, command_cursor) +
1281 command_buf.substr(command_cursor + 4);
1282 win->notify_screen_update();
1283 *commandhistory_itr = command_buf;
1284 return;
1285 case SDLK_BACKSPACE:
1286 if(command_cursor > 0) {
1287 command_buf = command_buf.substr(0, command_cursor - 4) +
1288 command_buf.substr(command_cursor);
1289 command_cursor -= 4;
1291 win->notify_screen_update();
1292 *commandhistory_itr = command_buf;
1293 return;
1294 default:
1295 break;
1298 //Not a special editing operation, insert/overwrite a character.
1299 uint32_t code = k.unicode;
1300 if(!code)
1301 return;
1302 uint8_t c1 = 33 + ((code >> 18) & 0x3F);
1303 uint8_t c2 = 33 + ((code >> 12) & 0x3F);
1304 uint8_t c3 = 33 + ((code >> 6) & 0x3F);
1305 uint8_t c4 = 33 + (code & 0x3F);
1306 if(command_overwrite && command_cursor < command_buf.length()) {
1307 command_buf[command_cursor] = c1;
1308 command_buf[command_cursor + 1] = c2;
1309 command_buf[command_cursor + 2] = c3;
1310 command_buf[command_cursor + 3] = c4;
1311 command_cursor += 4;
1312 } else {
1313 std::string foo = " ";
1314 foo[0] = c1;
1315 foo[1] = c2;
1316 foo[2] = c3;
1317 foo[3] = c4;
1318 command_buf = command_buf.substr(0, command_cursor) + foo + command_buf.substr(command_cursor);
1319 command_cursor += 4;
1321 *commandhistory_itr = command_buf;
1322 win->notify_screen_update();
1325 void do_event(SDL_Event& e) throw(std::bad_alloc)
1327 alarm(WATCHDOG_TIMEOUT);
1328 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE && e.key.keysym.mod == (KMOD_LCTRL |
1329 KMOD_LALT))
1330 exit(1);
1331 if(e.type == SDL_USEREVENT && e.user.code == 0) {
1332 if(screen_is_dirty)
1333 win->notify_screen_update();
1335 SDLKey key;
1336 get_ticks_msec();
1337 if(e.type == SDL_ACTIVEEVENT && e.active.gain && e.active.state == SDL_APPACTIVE) {
1338 win->notify_screen_update();
1339 return;
1341 if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
1342 key = e.key.keysym.sym;
1344 if(e.type == SDL_QUIT && state == WINSTATE_IDENTIFY)
1345 return;
1346 if(e.type == SDL_QUIT && state == WINSTATE_MODAL) {
1347 delayed_close_flag = true;
1348 return;
1350 if(e.type == SDL_QUIT) {
1351 command::invokeC("quit-emulator", win);
1352 state = WINSTATE_NORMAL;
1353 return;
1356 switch(state) {
1357 case WINSTATE_NORMAL:
1358 if(e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
1359 int32_t xc = e.button.x;
1360 int32_t yc = e.button.y;
1361 xc = (xc - 6 - vc_xoffset) / vc_hscl;
1362 yc = (yc - 6 - vc_yoffset) / vc_vscl;
1363 if(e.button.button == SDL_BUTTON_LEFT) {
1364 if(e.button.state == SDL_PRESSED)
1365 mouse_mask |= 1;
1366 else
1367 mouse_mask &= ~1;
1369 if(e.button.button == SDL_BUTTON_MIDDLE) {
1370 if(e.button.state == SDL_PRESSED)
1371 mouse_mask |= 2;
1372 else
1373 mouse_mask &= ~2;
1375 if(e.button.button == SDL_BUTTON_RIGHT) {
1376 if(e.button.state == SDL_PRESSED)
1377 mouse_mask |= 4;
1378 else
1379 mouse_mask &= ~4;
1382 std::ostringstream x;
1383 x << "mouse_button " << xc << " " << yc << " " << mouse_mask;
1384 command::invokeC(x.str(), win);
1387 if(e.type == SDL_KEYDOWN && key == SDLK_ESCAPE)
1388 return;
1389 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
1390 state = WINSTATE_COMMAND;
1391 command_buf = "";
1392 command_cursor = 0;
1393 commandhistory.push_front("");
1394 if(commandhistory.size() > MAXHISTORY)
1395 commandhistory.pop_back();
1396 commandhistory_itr = commandhistory.begin();
1397 win->notify_screen_update();
1398 return;
1401 bool more = true;
1403 auto i = keymapper_helper_sdl::translate_event(e);
1404 std::string cmd;
1405 if(i.symbol.device != SDL_DEV_NONE)
1406 cmd = mapper.map(i.symbol, i.polarity);
1407 if(cmd != "")
1408 command::invokeC(cmd, win);
1409 more = i.more;
1411 return;
1413 break;
1414 case WINSTATE_MODAL:
1415 if(e.type == SDL_KEYUP && key == SDLK_ESCAPE) {
1416 state = WINSTATE_NORMAL;
1417 modconfirm = false;
1418 modal_return_flag = true;
1419 modmsg = "";
1420 win->notify_screen_update(true);
1421 return;
1423 if(e.type == SDL_KEYUP && (key == SDLK_RETURN || key == SDLK_KP_ENTER)) {
1424 state = WINSTATE_NORMAL;
1425 modal_return_flag = true;
1426 modmsg = "";
1427 win->notify_screen_update(true);
1428 return;
1430 break;
1431 case WINSTATE_COMMAND:
1432 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) {
1433 state = WINSTATE_NORMAL;
1434 command_buf = "";
1435 win->notify_screen_update();
1436 if(commandhistory.front() == "")
1437 commandhistory.pop_front();
1438 return;
1440 if(e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_RETURN ||
1441 e.key.keysym.sym == SDLK_KP_ENTER)) {
1442 state = WINSTATE_NORMAL;
1443 if(commandhistory.front() == "")
1444 commandhistory.pop_front();
1445 command::invokeC(decode_string(command_buf), win);
1446 command_buf = "";
1447 win->notify_screen_update();
1448 autorepeat_phase = 0;
1449 return;
1451 if(e.type == SDL_KEYDOWN) {
1452 autorepeating_key = e.key.keysym;
1453 autorepeat_phase = 1;
1454 autorepeat_timecounter = 0;
1455 do_keyboard_command_edit(e.key.keysym);
1456 } else if(e.type == SDL_KEYUP) {
1457 autorepeat_phase = 0;
1459 if(e.type == SDL_USEREVENT && e.user.code == 0) {
1460 autorepeat_timecounter++;
1461 if(!autorepeat_phase)
1462 break;
1463 unsigned timeout = (autorepeat_phase == 1) ? autorepeat_first : autorepeat_subsequent;
1464 if(autorepeat_timecounter >= timeout) {
1465 do_keyboard_command_edit(autorepeating_key);
1466 autorepeat_timecounter = 0;
1467 autorepeat_phase = 2;
1470 break;
1471 case WINSTATE_IDENTIFY:
1472 auto i = keymapper_helper_sdl::translate_event(e);
1473 if(!i.polarity && i.symbol.device != SDL_DEV_NONE)
1474 win->modal_message(keymapper_helper_sdl::print_key_info(i.symbol), false);
1475 break;
1480 window::window()
1482 signal(SIGALRM, sigalrm_handler);
1483 alarm(WATCHDOG_TIMEOUT);
1484 system_log.open("lsnes.log", std::ios_base::out | std::ios_base::app);
1485 time_t curtime = time(NULL);
1486 struct tm* tm = localtime(&curtime);
1487 char buffer[1024];
1488 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1489 system_log << "-----------------------------------------------------------------------" << std::endl;
1490 system_log << "lsnes started at " << buffer << std::endl;
1491 system_log << "-----------------------------------------------------------------------" << std::endl;
1492 if(!sdl_init) {
1493 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
1494 SDL_EnableUNICODE(true);
1495 sdl_init = true;
1496 tid = SDL_AddTimer(MIN_UPDATE_TIME, timer_cb, NULL);
1498 i = NULL;
1499 win = this;
1500 state = WINSTATE_NORMAL;
1501 current_screen = NULL;
1502 pause_active = false;
1503 hwsurf = NULL;
1504 command_overwrite = false;
1505 old_screen_h = 0;
1506 old_screen_w = 0;
1507 modal_return_flag = false;
1508 delayed_close_flag = false;
1509 messagebuffer_next_seq = 0;
1510 messagebuffer_first_seq = 0;
1511 messagebuffer_first_show = 0;
1512 console_mode = false;
1513 maxmessages = MAXMESSAGES;
1515 notify_screen_update();
1516 std::string windowname = "lsnes-" + lsnes_version + "[" + bsnes_core_version + "]";
1517 SDL_WM_SetCaption(windowname.c_str(), "lsnes");
1519 SDL_AudioSpec* desired = new SDL_AudioSpec();
1520 SDL_AudioSpec* obtained = new SDL_AudioSpec();
1522 desired->freq = 44100;
1523 desired->format = AUDIO_S16SYS;
1524 desired->channels = 2;
1525 desired->samples = 8192;
1526 desired->callback = audiocb;
1527 desired->userdata = NULL;
1529 if(SDL_OpenAudio(desired, obtained) < 0) {
1530 message("Audio can't be initialized, audio playback disabled");
1531 return;
1534 //Fill the parameters.
1535 calculate_sampledup(obtained->freq);
1536 format = obtained->format;
1537 stereo = (obtained->channels == 2);
1538 //GO!!!
1539 SDL_PauseAudio(0);
1542 window::~window()
1544 time_t curtime = time(NULL);
1545 struct tm* tm = localtime(&curtime);
1546 char buffer[1024];
1547 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
1548 system_log << "-----------------------------------------------------------------------" << std::endl;
1549 system_log << "lsnes shutting down at " << buffer << std::endl;
1550 system_log << "-----------------------------------------------------------------------" << std::endl;
1551 system_log.close();
1552 if(sdl_init) {
1553 SDL_RemoveTimer(tid);
1554 SDL_Quit();
1555 sdl_init = false;
1559 bool window::modal_message(const std::string& msg, bool confirm) throw(std::bad_alloc)
1561 modconfirm = confirm;
1562 modmsg = msg;
1563 state = WINSTATE_MODAL;
1564 notify_screen_update();
1565 poll_inputs();
1566 bool ret = modconfirm;
1567 if(delayed_close_flag) {
1568 delayed_close_flag = false;
1569 command::invokeC("quit-emulator", win);
1571 return ret;
1574 void window::message(const std::string& msg) throw(std::bad_alloc)
1576 std::string msg2 = msg;
1577 bool locked_mode = (messagebuffer_next_seq - messagebuffer_first_show <= maxmessages) ;
1578 while(msg2 != "") {
1579 size_t s = msg2.find_first_of("\n");
1580 std::string forlog;
1581 if(s >= msg2.length()) {
1582 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2);
1583 system_log << forlog << std::endl;
1584 break;
1585 } else {
1586 messagebuffer[messagebuffer_next_seq++] = (forlog = msg2.substr(0, s));
1587 system_log << forlog << std::endl;
1588 msg2 = msg2.substr(s + 1);
1592 if(locked_mode && messagebuffer_first_show + maxmessages < messagebuffer_next_seq)
1593 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1595 while(messagebuffer.size() > MSGHISTORY) {
1596 messagebuffer.erase(messagebuffer_first_seq++);
1597 if(messagebuffer_first_show < messagebuffer_first_seq)
1598 messagebuffer_first_show = messagebuffer_first_seq;
1600 notify_screen_update();
1603 void window::set_main_surface(screen& scr) throw()
1605 current_screen = &scr;
1606 notify_screen_update(true);
1609 void window::notify_screen_update(bool full) throw()
1611 uint64_t curtime = get_ticks_msec();
1612 if(!full && last_ui_update < curtime && last_ui_update + MIN_UPDATE_TIME > curtime) {
1613 screen_is_dirty = true;
1614 return;
1616 last_ui_update = curtime;
1617 screen_is_dirty = false;
1619 try {
1620 std::ostringstream y;
1621 y << get_framerate();
1622 emustatus["FPS"] = y.str();
1623 } catch(...) {
1626 std::string command_showas = decode_string(command_buf);
1627 uint32_t screen_w = 512;
1628 uint32_t screen_h = 448;
1629 if(current_screen && current_screen->width >= 512 && current_screen->height >= 448) {
1630 screen_w = current_screen->width;
1631 screen_h = current_screen->height;
1633 uint32_t win_w = ((screen_w < 512) ? 512 : ((screen_w + 15) / 16 * 16)) + 278;
1634 uint32_t win_h = screen_h + MAXMESSAGES * 16 + 48;
1635 if(!hwsurf || static_cast<uint32_t>(hwsurf->w) != win_w || static_cast<uint32_t>(hwsurf->h) != win_h ||
1636 old_screen_w != screen_w || old_screen_h != screen_h || full) {
1637 //Create/Resize the window.
1638 if(!hwsurf || static_cast<uint32_t>(hwsurf->w) != win_w || static_cast<uint32_t>(hwsurf->h) != win_h) {
1639 SDL_Surface* hwsurf2 = SDL_SetVideoMode(win_w, win_h, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
1640 if(!hwsurf2) {
1641 //We are in too fucked up state to even print error as message.
1642 std::cout << "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl;
1643 exit(1);
1645 hwsurf = hwsurf2;
1647 if(current_screen)
1648 current_screen->set_palette(hwsurf->format->Rshift, hwsurf->format->Gshift,
1649 hwsurf->format->Bshift);
1650 //Blank the screen and draw borders.
1651 SDL_LockSurface(hwsurf);
1652 memset(hwsurf->pixels, 0, win_h * hwsurf->pitch);
1653 uint32_t bordercolor = 255 << hwsurf->format->Gshift;
1654 if(console_mode) {
1655 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, 2, win_w - 2,
1656 win_h - 28, bordercolor, 2);
1657 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, win_h - 26,
1658 win_w - 2, win_h - 2, bordercolor, 2);
1659 } else {
1660 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, 2, screen_w + 10,
1661 screen_h + 10, bordercolor, 2);
1662 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, screen_w + 12, 2,
1663 screen_w + 276, screen_h + 10, bordercolor, 2);
1664 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2, screen_h + 12,
1665 win_w - 2, screen_h + MAXMESSAGES * 16 + 20, bordercolor, 2);
1666 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, 2,
1667 screen_h + MAXMESSAGES * 16 + 22, win_w - 2, screen_h + MAXMESSAGES * 16 + 46,
1668 bordercolor, 2);
1670 SDL_UnlockSurface(hwsurf);
1671 old_screen_w = screen_w;
1672 old_screen_h = screen_h;
1674 SDL_LockSurface(hwsurf);
1675 if(!console_mode) {
1676 if(current_screen) {
1677 //Draw main screen (blanking background if needed).
1678 if(screen_w < current_screen->width || screen_h < current_screen->height)
1679 for(uint32_t i = 6; i < screen_h + 6; i++)
1680 memset(reinterpret_cast<uint8_t*>(hwsurf->pixels) + i * hwsurf->pitch + 24, 0,
1681 4 * screen_w);
1682 for(uint32_t i = 0; i < current_screen->height; i++)
1683 memcpy(reinterpret_cast<uint8_t*>(hwsurf->pixels) + (i + 6) * hwsurf->pitch + 24,
1684 reinterpret_cast<uint8_t*>(current_screen->memory) + current_screen->pitch * i,
1685 4 * current_screen->width);
1686 } else {
1687 //Draw blank.
1688 for(uint32_t i = 6; i < screen_h + 6; i++)
1689 memset(reinterpret_cast<uint8_t*>(hwsurf->pixels) + i * hwsurf->pitch + 24, 0,
1690 4 * screen_w);
1692 //Draw status.
1693 uint32_t status_x = screen_w + 16;
1694 uint32_t status_y = 6;
1695 for(auto i = emustatus.begin(); i != emustatus.end(); i++) {
1696 std::string msg = i->first + " " + i->second;
1697 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, msg, status_x, status_y,
1698 256);
1699 status_y += 16;
1701 while(status_y - 6 < screen_h / 16 * 16) {
1702 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "", status_x, status_y,
1703 256);
1704 status_y += 16;
1707 //Draw messages.
1708 uint32_t message_y;
1709 if(!console_mode)
1710 message_y = screen_h + 16;
1711 else
1712 message_y = 6;
1713 for(size_t j = 0; j < maxmessages; j++)
1714 try {
1715 std::ostringstream o;
1716 if(messagebuffer_first_show + j < messagebuffer_next_seq)
1717 o << (messagebuffer_first_show + j + 1) << ": "
1718 << messagebuffer[messagebuffer_first_show + j];
1719 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, o.str(), 6,
1720 message_y + 16 * j, win_w - 12);
1721 } catch(...) {
1723 if(messagebuffer_next_seq - messagebuffer_first_show > maxmessages)
1724 try {
1725 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "--More--", win_w - 76,
1726 message_y + 16 * maxmessages - 16, 64);
1727 } catch(...) {
1730 //Draw command_buf.
1731 uint32_t command_y = win_h - 22;
1732 try {
1733 if(state == WINSTATE_COMMAND)
1734 draw_command(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, command_showas,
1735 command_cursor / 4, 6, command_y, win_w - 12, command_overwrite);
1736 else
1737 draw_string(reinterpret_cast<uint8_t*>(hwsurf->pixels), hwsurf->pitch, "", 6, command_y,
1738 win_w - 12);
1739 } catch(...) {
1741 //Draw modal dialog.
1742 if(state == WINSTATE_MODAL)
1743 try {
1744 draw_modal_dialog(hwsurf, modmsg, modconfirm);
1745 } catch(...) {
1747 SDL_UnlockSurface(hwsurf);
1748 SDL_Flip(hwsurf);
1751 void window::poll_inputs() throw(std::bad_alloc)
1753 SDL_Event e;
1754 while(1) {
1755 if(modal_return_flag) {
1756 modal_return_flag = false;
1757 return;
1759 if(state == WINSTATE_NORMAL && !pause_active && !SDL_PollEvent(&e))
1760 break;
1761 else if(state == WINSTATE_NORMAL && !pause_active)
1762 do_event(e);
1763 else if(SDL_WaitEvent(&e))
1764 do_event(e);
1768 void window::bind(std::string mod, std::string modmask, std::string keyname, std::string cmd) throw(std::bad_alloc,
1769 std::runtime_error)
1771 mapper.bind(mod, modmask, keyname, cmd);
1772 if(modmask == "")
1773 message("Key " + keyname + " bound to '" + cmd + "'");
1774 else
1775 message("Key " + mod + "/" + modmask + " " + keyname + " bound to '" + cmd + "'");
1778 void window::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
1779 std::runtime_error)
1781 mapper.unbind(mod, modmask, keyname);
1782 if(modmask == "")
1783 message("Key " + keyname + " unbound");
1784 else
1785 message("Key " + mod + "/" + modmask + " " + keyname + " unbound");
1788 std::map<std::string, std::string>& window::get_emustatus() throw()
1790 return emustatus;
1793 void window::dumpbindings() throw(std::bad_alloc)
1795 mapper.dumpbindings(this);
1798 void window::paused(bool enable) throw()
1800 pause_active = enable;
1801 notify_screen_update();
1804 void window::sound_enable(bool enable) throw()
1806 sound_enabled = enable;
1807 SDL_PauseAudio(enable ? 0 : 1);
1810 namespace
1812 class enable_sound_cmd : public command
1814 public:
1815 enable_sound_cmd() throw(std::bad_alloc) : command("enable-sound") {}
1816 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1818 std::string s = args;
1819 if(s == "on" || s == "true" || s == "1" || s == "enable" || s == "enabled")
1820 win->sound_enable(true);
1821 else if(s == "off" || s == "false" || s == "0" || s == "disable" || s == "disabled")
1822 win->sound_enable(false);
1823 else
1824 throw std::runtime_error("Bad sound setting");
1826 std::string get_short_help() throw(std::bad_alloc) { return "Enable/Disable sound"; }
1827 std::string get_long_help() throw(std::bad_alloc)
1829 return "Syntax: enable-sound <on/off>\n"
1830 "Enable or disable sound.\n";
1832 } enable_sound_o;
1834 class identify_cmd : public command
1836 public:
1837 identify_cmd() throw(std::bad_alloc) : command("identify-key") {}
1838 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1840 if(args != "")
1841 throw std::runtime_error("This command does not take arguments");
1842 identify();
1844 std::string get_short_help() throw(std::bad_alloc) { return "Identify a key"; }
1845 std::string get_long_help() throw(std::bad_alloc)
1847 return "Syntax: identify-key\n"
1848 "Identifies a (pseudo-)key.\n";
1850 } identify_o;
1852 class scrollup_cmd : public command
1854 public:
1855 scrollup_cmd() throw(std::bad_alloc) : command("scroll-up") {}
1856 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1858 if(args != "")
1859 throw std::runtime_error("This command does not take arguments");
1860 if(messagebuffer_first_show > maxmessages)
1861 messagebuffer_first_show -= maxmessages;
1862 else
1863 messagebuffer_first_show = 0;
1864 if(messagebuffer_first_show < messagebuffer_first_seq)
1865 messagebuffer_first_show = messagebuffer_first_seq;
1866 win->notify_screen_update();
1868 std::string get_short_help() throw(std::bad_alloc) { return "Scroll console back one page"; }
1869 std::string get_long_help() throw(std::bad_alloc)
1871 return "Syntax: scroll-up\n"
1872 "Scrolls message console backward one page.\n";
1874 } scrollup_o;
1876 class scrollfullup_cmd : public command
1878 public:
1879 scrollfullup_cmd() throw(std::bad_alloc) : command("scroll-fullup") {}
1880 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1882 if(args != "")
1883 throw std::runtime_error("This command does not take arguments");
1884 messagebuffer_first_show = messagebuffer_first_seq;
1885 win->notify_screen_update();
1887 std::string get_short_help() throw(std::bad_alloc) { return "Scroll console to beginning"; }
1888 std::string get_long_help() throw(std::bad_alloc)
1890 return "Syntax: scroll-fullup\n"
1891 "Scrolls message console to beginning.\n";
1893 } scrollfullup_o;
1895 class scrollfulldown_cmd : public command
1897 public:
1898 scrollfulldown_cmd() throw(std::bad_alloc) : command("scroll-fulldown") {}
1899 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1901 if(args != "")
1902 throw std::runtime_error("This command does not take arguments");
1903 if(messagebuffer_next_seq < maxmessages)
1904 messagebuffer_first_show = 0;
1905 else
1906 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1907 win->notify_screen_update();
1909 std::string get_short_help() throw(std::bad_alloc) { return "Scroll console to end"; }
1910 std::string get_long_help() throw(std::bad_alloc)
1912 return "Syntax: scroll-fulldown\n"
1913 "Scrolls message console to end.\n";
1915 } scrollfulldown_o;
1917 class scrolldown_cmd : public command
1919 public:
1920 scrolldown_cmd() throw(std::bad_alloc) : command("scroll-down") {}
1921 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1923 if(args != "")
1924 throw std::runtime_error("This command does not take arguments");
1925 messagebuffer_first_show += maxmessages;
1926 if(messagebuffer_next_seq < maxmessages)
1927 messagebuffer_first_show = 0;
1928 else if(messagebuffer_next_seq < messagebuffer_first_show + maxmessages)
1929 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1930 win->notify_screen_update();
1932 std::string get_short_help() throw(std::bad_alloc) { return "Scroll console one page forward"; }
1933 std::string get_long_help() throw(std::bad_alloc)
1935 return "Syntax: scroll-down\n"
1936 "Scrolls message console forward one page.\n";
1938 } scrolldown_o;
1940 class toggleconsole_cmd : public command
1942 public:
1943 toggleconsole_cmd() throw(std::bad_alloc) : command("toggle-console") {}
1944 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
1946 if(args != "")
1947 throw std::runtime_error("This command does not take arguments");
1948 console_mode = !console_mode;
1949 if(console_mode)
1950 maxmessages = hwsurf ? (hwsurf->h - 38) / 16 : 36;
1951 else
1952 maxmessages = MAXMESSAGES;
1953 if(messagebuffer_next_seq < maxmessages)
1954 messagebuffer_first_show = 0;
1955 else
1956 messagebuffer_first_show = messagebuffer_next_seq - maxmessages;
1957 win->notify_screen_update(true);
1959 std::string get_short_help() throw(std::bad_alloc) { return "Toggle console between small and full "
1960 "window"; }
1961 std::string get_long_help() throw(std::bad_alloc)
1963 return "Syntax: toggle-console\n"
1964 "Toggles console between small and large.\n";
1966 } toggleconsole_o;
1969 void window::wait_msec(uint64_t msec) throw(std::bad_alloc)
1971 wait_canceled = false;
1972 uint64_t basetime = get_ticks_msec();
1973 while(!wait_canceled) {
1974 if(msec > 10)
1975 SDL_Delay(10);
1976 else
1977 SDL_Delay(msec);
1978 SDL_Event e;
1979 while(SDL_PollEvent(&e))
1980 do_event(e);
1981 uint64_t passed = get_ticks_msec() - basetime;
1982 if(passed > msec)
1983 break;
1987 void window::fatal_error() throw()
1989 try {
1990 message("PANIC: Cannot continue, press ESC or close window to exit.");
1991 //Force redraw.
1992 notify_screen_update(true);
1993 } catch(...) {
1994 //Just crash.
1995 exit(1);
1997 time_t curtime = time(NULL);
1998 struct tm* tm = localtime(&curtime);
1999 char buffer[1024];
2000 strftime(buffer, 1023, "%Y-%m-%d %H:%M:%S %Z", tm);
2001 system_log << "-----------------------------------------------------------------------" << std::endl;
2002 system_log << "lsnes paniced at " << buffer << std::endl;
2003 system_log << "-----------------------------------------------------------------------" << std::endl;
2004 system_log.close();
2005 while(true) {
2006 SDL_Event e;
2007 if(SDL_WaitEvent(&e)) {
2008 if(e.type == SDL_QUIT)
2009 exit(1);
2010 if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
2011 exit(1);
2016 uint64_t get_ticks_msec() throw()
2018 static uint64_t tickbase = 0;
2019 static Uint32 last_ticks = 0;
2020 Uint32 cur_ticks = SDL_GetTicks();
2021 if(last_ticks > cur_ticks)
2022 tickbase += 0x100000000ULL;
2023 last_ticks = cur_ticks;
2024 return tickbase + cur_ticks;
2027 void window::cancel_wait() throw()
2029 wait_canceled = true;
2032 void window::play_audio_sample(uint16_t left, uint16_t right) throw()
2034 sampledup_ctr += sampledup_inc;
2035 while(sampledup_ctr < sampledup_mod) {
2036 audiobuf[audiobuf_put++] = left;
2037 audiobuf[audiobuf_put++] = right;
2038 if(audiobuf_put == audiobuf_size)
2039 audiobuf_put = 0;
2040 sampledup_ctr += sampledup_inc;
2042 sampledup_ctr -= sampledup_mod;
2045 void window::set_window_compensation(uint32_t xoffset, uint32_t yoffset, uint32_t hscl, uint32_t vscl)
2047 vc_xoffset = xoffset;
2048 vc_yoffset = yoffset;
2049 vc_hscl = hscl;
2050 vc_vscl = vscl;