6 #include "settings.hpp"
10 #include "keymapper.hpp"
11 #include "framerate.hpp"
12 #include "fieldsplit.hpp"
16 #define WATCHDOG_TIMEOUT 15
18 #define MSGHISTORY 1000
19 #define MAXHISTORY 1000
20 #define JOYTHRESHOLD 3200
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
45 struct internal_keysymbol
53 bool operator==(const internal_keysymbol
& k
) const;
55 struct translated_event
57 translated_event(keysymbol _symbol
, bool _polarity
, bool _more
= false);
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)
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()
124 void calibration::update(short val
) throw()
141 void calibration::centered() throw()
146 short calibration::analog(short val
) throw()
155 return static_cast<short>(-32768.0 * (val
- left
) / (center
- left
));
157 return static_cast<short>(32767.0 * (val
- center
) / (right
- center
));
161 short calibration::digital(short val
) throw()
163 short a
= analog(val
);
164 if(a
< -JOYTHRESHOLD
)
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();
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
)
197 Uint32
timer_cb(Uint32 interval
, void* param
)
200 e
.type
= SDL_USEREVENT
;
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 }
237 {"backspace", SDLK_BACKSPACE
},
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
},
247 {"dollar", SDLK_DOLLAR
},
248 {"ampersand", SDLK_AMPERSAND
},
249 {"quote", SDLK_QUOTE
},
250 {"leftparen", SDLK_LEFTPAREN
},
251 {"rightparen", SDLK_RIGHTPAREN
},
252 {"asterisk", SDLK_ASTERISK
},
254 {"comma", SDLK_COMMA
},
255 {"minus", SDLK_MINUS
},
256 {"period", SDLK_PERIOD
},
257 {"slash", SDLK_SLASH
},
268 {"colon", SDLK_COLON
},
269 {"semicolon", SDLK_SEMICOLON
},
271 {"equals", SDLK_EQUALS
},
272 {"greater", SDLK_GREATER
},
273 {"question", SDLK_QUESTION
},
275 {"leftbracket", SDLK_LEFTBRACKET
},
276 {"backslash", SDLK_BACKSLASH
},
277 {"rightbracket", SDLK_RIGHTBRACKET
},
278 {"caret", SDLK_CARET
},
279 {"underscore", SDLK_UNDERSCORE
},
280 {"backquote", SDLK_BACKQUOTE
},
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
},
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
},
423 {"right", SDLK_RIGHT
},
425 {"insert", SDLK_INSERT
},
428 {"pageup", SDLK_PAGEUP
},
429 {"pagedown", SDLK_PAGEDOWN
},
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
},
454 {"rmeta", SDLK_RMETA
},
455 {"lmeta", SDLK_LMETA
},
456 {"lsuper", SDLK_LSUPER
},
457 {"rsuper", SDLK_RSUPER
},
459 {"compose", SDLK_COMPOSE
},
461 {"print", SDLK_PRINT
},
462 {"sysreq", SDLK_SYSREQ
},
463 {"break", SDLK_BREAK
},
465 {"power", SDLK_POWER
},
471 unsigned recognize_one_modifier(std::string mod
)
473 struct modifier
* m
= modifiers_table
;
475 if(mod
== std::string(m
->name
))
476 return m
->asmodifier
;
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};
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
)
512 polarity
= _polarity
;
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
);
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
];
528 bool polarity
= false;
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
;
538 k
.device
= SDL_DEV_JOYHAT
;
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
];
546 bool polarity
= false;
549 k
.device
= SDL_DEV_NONE
;
550 return translated_event(k
, false);
551 } else if(ov
== -1) {
556 axis
|= ((v
== 1) ? 0x10000 : 0);
563 k
.device
= SDL_DEV_JOYAXIS
;
565 return translated_event(k
, polarity
, more
);
566 } else if(e
.type
== SDL_JOYBUTTONUP
|| e
.type
== SDL_JOYBUTTONDOWN
) {
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
) {
574 k
.device
= SDL_DEV_KEYBOARD
;
575 k
.keyboard
= e
.key
.keysym
;
576 return translated_event(k
, (e
.type
== SDL_KEYDOWN
));
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))
600 //For the same hat, if there is overlap, then they compare equal.
601 return (((joyhat
& k
.joyhat
) >> 16) != 0);
606 unsigned keymapper_helper_sdl::mod_str(std::string mod
) throw(std::bad_alloc
, std::runtime_error
)
610 size_t split
= mod
.find_first_of(",");
612 if(split
< mod
.length()) {
613 omod
= mod
.substr(0, split
);
614 mod
= mod
.substr(split
+ 1);
616 throw std::runtime_error("Invalid modifier specification (trailing comma)");
621 ret
|= recognize_one_modifier(omod
);
626 unsigned keymapper_helper_sdl::mod_key(keymapper_helper_sdl::keysymbol key
) throw(std::bad_alloc
)
628 if(key
.device
!= SDL_DEV_KEYBOARD
)
630 struct modifier
* m
= modifiers_table
;
633 if(!m
->synthethic
&& (key
.keyboard
.mod
& m
->sdlvalue
))
640 keymapper_helper_sdl::internal_keysymbol
keymapper_helper_sdl::key_str(std::string key
) throw(std::bad_alloc
,
643 if(key
.length() > 8 && key
.substr(0, 8) == "joystick") {
644 const char* rest
= key
.c_str() + 8;
646 unsigned subtype
= 0;
648 unsigned long value
= strtoul(rest
, &end
, 10);
650 throw std::runtime_error("Bad joystick number '" + key
+ "'");
652 if(!strncmp(end
, "button", 6)) {
655 } else if(!strncmp(end
, "axis", 4)) {
658 } else if(!strncmp(end
, "hat", 3)) {
662 throw std::runtime_error("Bad joystick subtype '" + key
+ "'");
663 value
= strtoul(rest
, &end
, 10);
666 if(value
> 255 || *end
)
667 throw std::runtime_error("Bad joystick button '" + key
+ "'");
668 internal_keysymbol k
;
669 k
.mode
= KEYTYPE_JOYBUTTON
;
672 } else if(subtype
== 2) {
673 if(!strncmp(end
, "+", 1) && value
<= 255)
675 else if(!strncmp(end
, "-", 1) && value
<= 255)
678 throw std::runtime_error("Bad joystick axis '" + key
+ "'");
679 internal_keysymbol k
;
680 k
.mode
= KEYTYPE_JOYAXIS
;
683 } else if(subtype
== 3) {
684 if(!strncmp(end
, "n", 1) && value
<= 255)
686 else if(!strncmp(end
, "e", 1) && value
<= 255)
688 else if(!strncmp(end
, "s", 1) && value
<= 255)
690 else if(!strncmp(end
, "w", 1) && value
<= 255)
693 throw std::runtime_error("Bad joystick hat '" + key
+ "'");
694 internal_keysymbol k
;
695 k
.mode
= KEYTYPE_JOYHAT
;
699 } if(key
.length() > 3 && key
.substr(0, 3) == "key") {
700 const char* rest
= key
.c_str() + 3;
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
;
711 struct key
* k
= keys_table
;
713 if(key
== std::string(k
->name
)) {
714 internal_keysymbol k2
;
715 k2
.mode
= KEYTYPE_SYMBOL
;
717 k2
.symbolic
= k
->symbol
;
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
;
743 k
.mode
= KEYTYPE_NONE
;
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
;
755 if((mod
& m
->asmodifier
) == m
->asmodifier
) {
756 ret
= ret
+ m
->name
+ ",";
760 ret
= ret
.substr(0, ret
.length() - 1);
764 if((modmask
& m
->asmodifier
) == m
->asmodifier
) {
765 ret
= ret
+ m
->name
+ ",";
769 ret
= ret
.substr(0, ret
.length() - 1);
772 if(key
.mode
== KEYTYPE_SCAN_SYMBOL
|| key
.mode
== KEYTYPE_SYMBOL
) {
773 struct key
* k
= keys_table
;
775 if(key
.symbolic
== k
->symbol
) {
781 } else if(key
.mode
== KEYTYPE_SCAN
) {
782 std::ostringstream x
;
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) ? '+' : '-');
790 } else if(key
.mode
== KEYTYPE_JOYBUTTON
) {
791 std::ostringstream x
;
792 x
<< "joystick" << ((key
.joybutton
>> 8) & 0xFF) << "button" << (key
.joybutton
& 0xFF);
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)
799 if(key
.joyhat
& 0x40000)
801 if(key
.joyhat
& 0x20000)
803 if(key
.joyhat
& 0x80000)
807 ret
= ret
+ "<invalid>";
812 std::string
keymapper_helper_sdl::print_key_info(keymapper_helper_sdl::keysymbol key
) throw(std::bad_alloc
)
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
;
820 if((mod
& m
->asmodifier
) == m
->asmodifier
) {
821 ret
= ret
+ m
->name
+ " ";
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
;
835 if(key
.keyboard
.sym
== k
->symbol
) {
836 ret
= ret
+ "Name: " + k
->name
+ "\n";
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)
855 if(key
.joyhat
& 0x20000)
857 if(key
.joyhat
& 0x40000)
859 if(key
.joyhat
& 0x80000)
861 ret
+ ret
+ "Name: " + x
.str() + "\n";
867 extern uint32_t fontdata
[];
872 uint32_t mouse_mask
= 0;
875 uint32_t vc_hscl
= 1;
876 uint32_t vc_vscl
= 1;
880 bool modal_return_flag
;
881 bool delayed_close_flag
;
883 std::string command_buf
;
884 bool command_overwrite
;
885 size_t command_cursor
;
886 unsigned old_screen_w
;
887 unsigned old_screen_h
;
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
;
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
;
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);
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
;
924 bool sound_enabled
= true;
926 void calculate_sampledup(uint32_t real_rate
)
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;
938 lprev
= rprev
= 32768;
939 uint16_t bias
= (format
== AUDIO_S8
|| format
== AUDIO_S16LSB
|| format
== AUDIO_S16MSB
|| format
==
940 AUDIO_S16SYS
) ? 32768 : 0;
943 if(audiobuf_get
== audiobuf_put
) {
947 l
= lprev
= audiobuf
[audiobuf_get
++];
948 r
= rprev
= audiobuf
[audiobuf_get
++];
949 if(audiobuf_get
== audiobuf_size
)
954 if(format
== AUDIO_U8
|| format
== AUDIO_S8
) {
955 stream
[0] = (l
- bias
) >> 8;
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
);
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;
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;
979 stream
[3] = (r
- bias
);
980 stream
[2] = (r
- bias
) >> 8;
982 stream
+= (stereo
? 4 : 2);
983 len
-= (stereo
? 4 : 2);
990 state
= WINSTATE_IDENTIFY
;
991 win
->message("Press key to identify.");
992 win
->notify_screen_update();
996 std::string
decode_string(std::string e
)
999 for(size_t i
= 0; i
< e
.length(); i
+= 4) {
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
;
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);
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);
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
);
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));
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));
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
;
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)
1079 if(c
== hilite_pos
&& hilite_mode
== 2)
1081 auto g
= find_glyph(*si
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
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;
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
)) &
1099 b
^= (j
>= curstart
);
1100 ptr
[old_x
+ i
] = b
? 0xFFFFFFFFU
: 0;
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)
1111 if(c
== hilite_pos
&& hilite_mode
== 2)
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
)
1139 uint32_t height
= 0;
1141 msg
= msg
+ "\n\nHit Enter to confirm, Esc to cancel";
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);
1156 if(width
+ 12 >= static_cast<uint32_t>(surf
->w
)) {
1161 x1
= (surf
->w
- width
) / 2;
1164 if(height
+ 12 >= static_cast<uint32_t>(surf
->h
)) {
1169 y1
= (surf
->h
- height
) / 2;
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,
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
)
1188 uint8_t* base
= reinterpret_cast<uint8_t*>(surf
->pixels
) + (y1
+ oy
) * surf
->pitch
+
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
)) &
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
)
1211 if(k
.sym
== SDLK_RETURN
)
1213 if(k
.sym
== SDLK_KP_ENTER
)
1215 //Map keys a bit if numlock is off.
1216 if((k
.mod
& KMOD_NUM
) == 0) {
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;
1233 //Special editing operations.
1236 command_overwrite
= !command_overwrite
;
1237 win
->notify_screen_update();
1240 command_cursor
= command_buf
.length();
1241 win
->notify_screen_update();
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();
1254 command_cursor
= (command_cursor
> 0) ? (command_cursor
- 4) : 0;
1255 win
->notify_screen_update();
1258 command_cursor
= (command_cursor
< command_buf
.length()) ? (command_cursor
+ 4) :
1259 command_buf
.length();
1260 win
->notify_screen_update();
1264 win
->notify_screen_update();
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();
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
;
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
;
1298 //Not a special editing operation, insert/overwrite a character.
1299 uint32_t code
= k
.unicode
;
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;
1313 std::string foo
= " ";
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
|
1331 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
1333 win
->notify_screen_update();
1337 if(e
.type
== SDL_ACTIVEEVENT
&& e
.active
.gain
&& e
.active
.state
== SDL_APPACTIVE
) {
1338 win
->notify_screen_update();
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
)
1346 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_MODAL
) {
1347 delayed_close_flag
= true;
1350 if(e
.type
== SDL_QUIT
) {
1351 command::invokeC("quit-emulator", win
);
1352 state
= WINSTATE_NORMAL
;
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
)
1369 if(e
.button
.button
== SDL_BUTTON_MIDDLE
) {
1370 if(e
.button
.state
== SDL_PRESSED
)
1375 if(e
.button
.button
== SDL_BUTTON_RIGHT
) {
1376 if(e
.button
.state
== SDL_PRESSED
)
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
)
1389 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
1390 state
= WINSTATE_COMMAND
;
1393 commandhistory
.push_front("");
1394 if(commandhistory
.size() > MAXHISTORY
)
1395 commandhistory
.pop_back();
1396 commandhistory_itr
= commandhistory
.begin();
1397 win
->notify_screen_update();
1403 auto i
= keymapper_helper_sdl::translate_event(e
);
1405 if(i
.symbol
.device
!= SDL_DEV_NONE
)
1406 cmd
= mapper
.map(i
.symbol
, i
.polarity
);
1408 command::invokeC(cmd
, win
);
1414 case WINSTATE_MODAL
:
1415 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
1416 state
= WINSTATE_NORMAL
;
1418 modal_return_flag
= true;
1420 win
->notify_screen_update(true);
1423 if(e
.type
== SDL_KEYUP
&& (key
== SDLK_RETURN
|| key
== SDLK_KP_ENTER
)) {
1424 state
= WINSTATE_NORMAL
;
1425 modal_return_flag
= true;
1427 win
->notify_screen_update(true);
1431 case WINSTATE_COMMAND
:
1432 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
) {
1433 state
= WINSTATE_NORMAL
;
1435 win
->notify_screen_update();
1436 if(commandhistory
.front() == "")
1437 commandhistory
.pop_front();
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
);
1447 win
->notify_screen_update();
1448 autorepeat_phase
= 0;
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
)
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;
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);
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
);
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
;
1493 SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
| SDL_INIT_JOYSTICK
| SDL_INIT_TIMER
);
1494 SDL_EnableUNICODE(true);
1496 tid
= SDL_AddTimer(MIN_UPDATE_TIME
, timer_cb
, NULL
);
1500 state
= WINSTATE_NORMAL
;
1501 current_screen
= NULL
;
1502 pause_active
= false;
1504 command_overwrite
= false;
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");
1534 //Fill the parameters.
1535 calculate_sampledup(obtained
->freq
);
1536 format
= obtained
->format
;
1537 stereo
= (obtained
->channels
== 2);
1544 time_t curtime
= time(NULL
);
1545 struct tm
* tm
= localtime(&curtime
);
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
;
1553 SDL_RemoveTimer(tid
);
1559 bool window::modal_message(const std::string
& msg
, bool confirm
) throw(std::bad_alloc
)
1561 modconfirm
= confirm
;
1563 state
= WINSTATE_MODAL
;
1564 notify_screen_update();
1566 bool ret
= modconfirm
;
1567 if(delayed_close_flag
) {
1568 delayed_close_flag
= false;
1569 command::invokeC("quit-emulator", win
);
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
) ;
1579 size_t s
= msg2
.find_first_of("\n");
1581 if(s
>= msg2
.length()) {
1582 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
);
1583 system_log
<< forlog
<< std::endl
;
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;
1616 last_ui_update
= curtime
;
1617 screen_is_dirty
= false;
1620 std::ostringstream y
;
1621 y
<< get_framerate();
1622 emustatus
["FPS"] = y
.str();
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
);
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
;
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
;
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);
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,
1670 SDL_UnlockSurface(hwsurf
);
1671 old_screen_w
= screen_w
;
1672 old_screen_h
= screen_h
;
1674 SDL_LockSurface(hwsurf
);
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,
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
);
1688 for(uint32_t i
= 6; i
< screen_h
+ 6; i
++)
1689 memset(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + i
* hwsurf
->pitch
+ 24, 0,
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
,
1701 while(status_y
- 6 < screen_h
/ 16 * 16) {
1702 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", status_x
, status_y
,
1710 message_y
= screen_h
+ 16;
1713 for(size_t j
= 0; j
< maxmessages
; j
++)
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);
1723 if(messagebuffer_next_seq
- messagebuffer_first_show
> maxmessages
)
1725 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "--More--", win_w
- 76,
1726 message_y
+ 16 * maxmessages
- 16, 64);
1731 uint32_t command_y
= win_h
- 22;
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
);
1737 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", 6, command_y
,
1741 //Draw modal dialog.
1742 if(state
== WINSTATE_MODAL
)
1744 draw_modal_dialog(hwsurf
, modmsg
, modconfirm
);
1747 SDL_UnlockSurface(hwsurf
);
1751 void window::poll_inputs() throw(std::bad_alloc
)
1755 if(modal_return_flag
) {
1756 modal_return_flag
= false;
1759 if(state
== WINSTATE_NORMAL
&& !pause_active
&& !SDL_PollEvent(&e
))
1761 else if(state
== WINSTATE_NORMAL
&& !pause_active
)
1763 else if(SDL_WaitEvent(&e
))
1768 void window::bind(std::string mod
, std::string modmask
, std::string keyname
, std::string cmd
) throw(std::bad_alloc
,
1771 mapper
.bind(mod
, modmask
, keyname
, cmd
);
1773 message("Key " + keyname
+ " bound to '" + cmd
+ "'");
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
,
1781 mapper
.unbind(mod
, modmask
, keyname
);
1783 message("Key " + keyname
+ " unbound");
1785 message("Key " + mod
+ "/" + modmask
+ " " + keyname
+ " unbound");
1788 std::map
<std::string
, std::string
>& window::get_emustatus() throw()
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);
1812 class enable_sound_cmd
: public command
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);
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";
1834 class identify_cmd
: public command
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
)
1841 throw std::runtime_error("This command does not take arguments");
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";
1852 class scrollup_cmd
: public command
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
)
1859 throw std::runtime_error("This command does not take arguments");
1860 if(messagebuffer_first_show
> maxmessages
)
1861 messagebuffer_first_show
-= maxmessages
;
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";
1876 class scrollfullup_cmd
: public command
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
)
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";
1895 class scrollfulldown_cmd
: public command
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
)
1902 throw std::runtime_error("This command does not take arguments");
1903 if(messagebuffer_next_seq
< maxmessages
)
1904 messagebuffer_first_show
= 0;
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";
1917 class scrolldown_cmd
: public command
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
)
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";
1940 class toggleconsole_cmd
: public command
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
)
1947 throw std::runtime_error("This command does not take arguments");
1948 console_mode
= !console_mode
;
1950 maxmessages
= hwsurf
? (hwsurf
->h
- 38) / 16 : 36;
1952 maxmessages
= MAXMESSAGES
;
1953 if(messagebuffer_next_seq
< maxmessages
)
1954 messagebuffer_first_show
= 0;
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 "
1961 std::string
get_long_help() throw(std::bad_alloc
)
1963 return "Syntax: toggle-console\n"
1964 "Toggles console between small and large.\n";
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
) {
1979 while(SDL_PollEvent(&e
))
1981 uint64_t passed
= get_ticks_msec() - basetime
;
1987 void window::fatal_error() throw()
1990 message("PANIC: Cannot continue, press ESC or close window to exit.");
1992 notify_screen_update(true);
1997 time_t curtime
= time(NULL
);
1998 struct tm
* tm
= localtime(&curtime
);
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
;
2007 if(SDL_WaitEvent(&e
)) {
2008 if(e
.type
== SDL_QUIT
)
2010 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
)
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
)
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
;