6 #include "settings.hpp"
10 #include "keymapper.hpp"
11 #include "framerate.hpp"
15 #define WATCHDOG_TIMEOUT 15
17 #define MSGHISTORY 1000
18 #define MAXHISTORY 1000
19 #define JOYTHRESHOLD 3200
26 #define SDL_DEV_NONE 0
27 #define SDL_DEV_KEYBOARD 1
28 #define SDL_DEV_JOYAXIS 2
29 #define SDL_DEV_JOYBUTTON 3
30 #define SDL_DEV_JOYHAT 4
31 // Limit the emulator to ~30fps.
32 #define MIN_UPDATE_TIME 33
34 class keymapper_helper_sdl
44 struct internal_keysymbol
52 bool operator==(const internal_keysymbol
& k
) const;
54 struct translated_event
56 translated_event(keysymbol _symbol
, bool _polarity
, bool _more
= false);
61 static translated_event
translate_event(SDL_Event
& e
) throw(std::bad_alloc
, std::runtime_error
);
62 static unsigned mod_str(std::string mod
) throw(std::bad_alloc
, std::runtime_error
);
63 static unsigned mod_key(keysymbol key
) throw(std::bad_alloc
);
64 static internal_keysymbol
key_str(std::string key
) throw(std::bad_alloc
, std::runtime_error
);
65 static internal_keysymbol
key_key(keysymbol key
) throw(std::bad_alloc
);
66 static std::string
name_key(unsigned mod
, unsigned modmask
, struct internal_keysymbol key
)
67 throw(std::bad_alloc
);
68 static std::string
print_key_info(keysymbol key
) throw(std::bad_alloc
);
71 #define KEYTYPE_SCAN_SYMBOL 0
72 #define KEYTYPE_SCAN 1
73 #define KEYTYPE_SYMBOL 2
74 #define KEYTYPE_JOYAXIS 3
75 #define KEYTYPE_JOYHAT 4
76 #define KEYTYPE_JOYBUTTON 5
77 #define KEYTYPE_NONE 6
79 #define BARE_CTRL 0x0001
80 #define BARE_LCTRL 0x0002
81 #define BARE_RCTRL 0x0004
82 #define BARE_NUM 0x0008
83 #define BARE_SHIFT 0x0010
84 #define BARE_LSHIFT 0x0020
85 #define BARE_RSHIFT 0x0040
86 #define BARE_CAPS 0x0080
87 #define BARE_ALT 0x0100
88 #define BARE_LALT 0x0200
89 #define BARE_RALT 0x0400
90 #define BARE_MODE 0x0800
91 #define BARE_META 0x1000
92 #define BARE_LMETA 0x2000
93 #define BARE_RMETA 0x4000
95 #define LCTRL_VALUE (BARE_CTRL | BARE_LCTRL)
96 #define RCTRL_VALUE (BARE_CTRL | BARE_RCTRL)
97 #define LALT_VALUE (BARE_ALT | BARE_LALT)
98 #define RALT_VALUE (BARE_ALT | BARE_RALT)
99 #define LSHIFT_VALUE (BARE_SHIFT | BARE_LSHIFT)
100 #define RSHIFT_VALUE (BARE_SHIFT | BARE_RSHIFT)
101 #define LMETA_VALUE (BARE_META | BARE_LMETA)
102 #define RMETA_VALUE (BARE_META | BARE_RMETA)
112 void update(short val
) throw();
113 void centered() throw();
114 short analog(short val
) throw();
115 short digital(short val
) throw();
118 calibration::calibration()
123 void calibration::update(short val
) throw()
140 void calibration::centered() throw()
145 short calibration::analog(short val
) throw()
154 return static_cast<short>(-32768.0 * (val
- left
) / (center
- left
));
156 return static_cast<short>(32767.0 * (val
- center
) / (right
- center
));
160 short calibration::digital(short val
) throw()
162 short a
= analog(val
);
163 if(a
< -JOYTHRESHOLD
)
170 std::map
<unsigned short, calibration
> calibrations
;
172 void handle_calibration_event(SDL_JoyAxisEvent
& e
)
174 unsigned short num
= static_cast<unsigned short>(e
.which
) * 256 + static_cast<unsigned short>(e
.axis
);
175 calibrations
[num
].update(e
.value
);
178 void exit_calibration()
180 for(auto i
= calibrations
.begin(); i
!= calibrations
.end(); i
++)
181 i
->second
.centered();
188 std::map
<unsigned short, int> axis_current_state
; //-1, 0 or 1.
189 std::map
<unsigned short, unsigned> hat_current_state
; //1 for up, 2 for right, 4 for down, 8 for left.
191 void sigalrm_handler(int s
)
196 Uint32
timer_cb(Uint32 interval
, void* param
)
199 e
.type
= SDL_USEREVENT
;
212 } modifiers_table
[] = {
213 { "ctrl", true, 0, 0, 0x0001 },
214 { "lctrl", false, KMOD_LCTRL
, 0x0003, 0x0002 },
215 { "rctrl", false, KMOD_RCTRL
, 0x0005, 0x0004 },
216 { "alt", true, 0, 0, 0x0008 },
217 { "lalt", false, KMOD_LALT
, 0x0018, 0x0010 },
218 { "ralt", false, KMOD_RALT
, 0x0028, 0x0020 },
219 { "shift", true, 0, 0, 0x0040 },
220 { "lshift", false, KMOD_LSHIFT
, 0x00C0, 0x0080 },
221 { "rshift", false, KMOD_RSHIFT
, 0x0140, 0x0100 },
222 { "meta", true, 0, 0, 0x0200 },
223 { "lmeta", false, KMOD_LMETA
, 0x0600, 0x0400 },
224 { "rmeta", false, KMOD_RMETA
, 0x0A00, 0x0800 },
225 { "num", false, KMOD_NUM
, 0x1000, 0x1000 },
226 { "caps", false, KMOD_CAPS
, 0x2000, 0x2000 },
227 { "mode", false, KMOD_MODE
, 0x4000, 0x4000 },
228 { NULL
, false, 0, 0, 0 }
236 {"backspace", SDLK_BACKSPACE
},
238 {"clear", SDLK_CLEAR
},
239 {"return", SDLK_RETURN
},
240 {"pause", SDLK_PAUSE
},
241 {"escape", SDLK_ESCAPE
},
242 {"space", SDLK_SPACE
},
243 {"exclaim", SDLK_EXCLAIM
},
244 {"quotedbl", SDLK_QUOTEDBL
},
246 {"dollar", SDLK_DOLLAR
},
247 {"ampersand", SDLK_AMPERSAND
},
248 {"quote", SDLK_QUOTE
},
249 {"leftparen", SDLK_LEFTPAREN
},
250 {"rightparen", SDLK_RIGHTPAREN
},
251 {"asterisk", SDLK_ASTERISK
},
253 {"comma", SDLK_COMMA
},
254 {"minus", SDLK_MINUS
},
255 {"period", SDLK_PERIOD
},
256 {"slash", SDLK_SLASH
},
267 {"colon", SDLK_COLON
},
268 {"semicolon", SDLK_SEMICOLON
},
270 {"equals", SDLK_EQUALS
},
271 {"greater", SDLK_GREATER
},
272 {"question", SDLK_QUESTION
},
274 {"leftbracket", SDLK_LEFTBRACKET
},
275 {"backslash", SDLK_BACKSLASH
},
276 {"rightbracket", SDLK_RIGHTBRACKET
},
277 {"caret", SDLK_CARET
},
278 {"underscore", SDLK_UNDERSCORE
},
279 {"backquote", SDLK_BACKQUOTE
},
306 {"delete", SDLK_DELETE
},
307 {"world_0", SDLK_WORLD_0
},
308 {"world_1", SDLK_WORLD_1
},
309 {"world_2", SDLK_WORLD_2
},
310 {"world_3", SDLK_WORLD_3
},
311 {"world_4", SDLK_WORLD_4
},
312 {"world_5", SDLK_WORLD_5
},
313 {"world_6", SDLK_WORLD_6
},
314 {"world_7", SDLK_WORLD_7
},
315 {"world_8", SDLK_WORLD_8
},
316 {"world_9", SDLK_WORLD_9
},
317 {"world_10", SDLK_WORLD_10
},
318 {"world_11", SDLK_WORLD_11
},
319 {"world_12", SDLK_WORLD_12
},
320 {"world_13", SDLK_WORLD_13
},
321 {"world_14", SDLK_WORLD_14
},
322 {"world_15", SDLK_WORLD_15
},
323 {"world_16", SDLK_WORLD_16
},
324 {"world_17", SDLK_WORLD_17
},
325 {"world_18", SDLK_WORLD_18
},
326 {"world_19", SDLK_WORLD_19
},
327 {"world_20", SDLK_WORLD_20
},
328 {"world_21", SDLK_WORLD_21
},
329 {"world_22", SDLK_WORLD_22
},
330 {"world_23", SDLK_WORLD_23
},
331 {"world_24", SDLK_WORLD_24
},
332 {"world_25", SDLK_WORLD_25
},
333 {"world_26", SDLK_WORLD_26
},
334 {"world_27", SDLK_WORLD_27
},
335 {"world_28", SDLK_WORLD_28
},
336 {"world_29", SDLK_WORLD_29
},
337 {"world_30", SDLK_WORLD_30
},
338 {"world_31", SDLK_WORLD_31
},
339 {"world_32", SDLK_WORLD_32
},
340 {"world_33", SDLK_WORLD_33
},
341 {"world_34", SDLK_WORLD_34
},
342 {"world_35", SDLK_WORLD_35
},
343 {"world_36", SDLK_WORLD_36
},
344 {"world_37", SDLK_WORLD_37
},
345 {"world_38", SDLK_WORLD_38
},
346 {"world_39", SDLK_WORLD_39
},
347 {"world_40", SDLK_WORLD_40
},
348 {"world_41", SDLK_WORLD_41
},
349 {"world_42", SDLK_WORLD_42
},
350 {"world_43", SDLK_WORLD_43
},
351 {"world_44", SDLK_WORLD_44
},
352 {"world_45", SDLK_WORLD_45
},
353 {"world_46", SDLK_WORLD_46
},
354 {"world_47", SDLK_WORLD_47
},
355 {"world_48", SDLK_WORLD_48
},
356 {"world_49", SDLK_WORLD_49
},
357 {"world_50", SDLK_WORLD_50
},
358 {"world_51", SDLK_WORLD_51
},
359 {"world_52", SDLK_WORLD_52
},
360 {"world_53", SDLK_WORLD_53
},
361 {"world_54", SDLK_WORLD_54
},
362 {"world_55", SDLK_WORLD_55
},
363 {"world_56", SDLK_WORLD_56
},
364 {"world_57", SDLK_WORLD_57
},
365 {"world_58", SDLK_WORLD_58
},
366 {"world_59", SDLK_WORLD_59
},
367 {"world_60", SDLK_WORLD_60
},
368 {"world_61", SDLK_WORLD_61
},
369 {"world_62", SDLK_WORLD_62
},
370 {"world_63", SDLK_WORLD_63
},
371 {"world_64", SDLK_WORLD_64
},
372 {"world_65", SDLK_WORLD_65
},
373 {"world_66", SDLK_WORLD_66
},
374 {"world_67", SDLK_WORLD_67
},
375 {"world_68", SDLK_WORLD_68
},
376 {"world_69", SDLK_WORLD_69
},
377 {"world_70", SDLK_WORLD_70
},
378 {"world_71", SDLK_WORLD_71
},
379 {"world_72", SDLK_WORLD_72
},
380 {"world_73", SDLK_WORLD_73
},
381 {"world_74", SDLK_WORLD_74
},
382 {"world_75", SDLK_WORLD_75
},
383 {"world_76", SDLK_WORLD_76
},
384 {"world_77", SDLK_WORLD_77
},
385 {"world_78", SDLK_WORLD_78
},
386 {"world_79", SDLK_WORLD_79
},
387 {"world_80", SDLK_WORLD_80
},
388 {"world_81", SDLK_WORLD_81
},
389 {"world_82", SDLK_WORLD_82
},
390 {"world_83", SDLK_WORLD_83
},
391 {"world_84", SDLK_WORLD_84
},
392 {"world_85", SDLK_WORLD_85
},
393 {"world_86", SDLK_WORLD_86
},
394 {"world_87", SDLK_WORLD_87
},
395 {"world_88", SDLK_WORLD_88
},
396 {"world_89", SDLK_WORLD_89
},
397 {"world_90", SDLK_WORLD_90
},
398 {"world_91", SDLK_WORLD_91
},
399 {"world_92", SDLK_WORLD_92
},
400 {"world_93", SDLK_WORLD_93
},
401 {"world_94", SDLK_WORLD_94
},
402 {"world_95", SDLK_WORLD_95
},
413 {"kp_period", SDLK_KP_PERIOD
},
414 {"kp_divide", SDLK_KP_DIVIDE
},
415 {"kp_multiply", SDLK_KP_MULTIPLY
},
416 {"kp_minus", SDLK_KP_MINUS
},
417 {"kp_plus", SDLK_KP_PLUS
},
418 {"kp_enter", SDLK_KP_ENTER
},
419 {"kp_equals", SDLK_KP_EQUALS
},
422 {"right", SDLK_RIGHT
},
424 {"insert", SDLK_INSERT
},
427 {"pageup", SDLK_PAGEUP
},
428 {"pagedown", SDLK_PAGEDOWN
},
444 {"numlock", SDLK_NUMLOCK
},
445 {"capslock", SDLK_CAPSLOCK
},
446 {"scrollock", SDLK_SCROLLOCK
},
447 {"rshift", SDLK_RSHIFT
},
448 {"lshift", SDLK_LSHIFT
},
449 {"rctrl", SDLK_RCTRL
},
450 {"lctrl", SDLK_LCTRL
},
453 {"rmeta", SDLK_RMETA
},
454 {"lmeta", SDLK_LMETA
},
455 {"lsuper", SDLK_LSUPER
},
456 {"rsuper", SDLK_RSUPER
},
458 {"compose", SDLK_COMPOSE
},
460 {"print", SDLK_PRINT
},
461 {"sysreq", SDLK_SYSREQ
},
462 {"break", SDLK_BREAK
},
464 {"power", SDLK_POWER
},
470 unsigned recognize_one_modifier(std::string mod
)
472 struct modifier
* m
= modifiers_table
;
474 if(mod
== std::string(m
->name
))
475 return m
->asmodifier
;
478 throw std::runtime_error("Unknown modifier '" + mod
+ "'");
481 uint32_t transpack
[16] = {0, 1, 3, 2, 5, 4, 0, 7, 8, 0, 0, 6, 0, 0, 0};
482 uint32_t polarities
[9] = {1, 1, 1, 1, 0, 0, 0, 0, 0};
483 uint32_t directions
[9] = {1, 2, 4, 8, 1, 2, 4, 8, 0};
486 0x032221008ULL
, //From center.
487 0x344444184ULL
, //From up.
488 0x555544855ULL
, //From up&right.
489 0x555528055ULL
, //From right.
490 0x555586655ULL
, //From down&right.
491 0x663816666ULL
, //From down.
492 0x668777777ULL
, //From down&left.
493 0x082777777ULL
, //From left.
494 0x844777777ULL
//From up&left.
497 void hat_transition(uint32_t& ov
, uint32_t& v
, bool& polarity
, bool& more
)
499 uint32_t t1
= transpack
[ov
];
500 uint32_t t2
= transpack
[v
];
501 more
= (v
!= ((X
[t1
] >> 4 * t2
) & 0xF));
502 polarity
= polarities
[(X
[t1
] >> 4 * t2
) & 0xF];
503 ov
^= directions
[(X
[t1
] >> 4 * t2
) & 0xF];
504 v
= directions
[(X
[t1
] >> 4 * t2
) & 0xF];
508 keymapper_helper_sdl::translated_event::translated_event(keysymbol _symbol
, bool _polarity
, bool _more
)
511 polarity
= _polarity
;
515 keymapper_helper_sdl::translated_event
keymapper_helper_sdl::translate_event(SDL_Event
& e
)
516 throw(std::bad_alloc
, std::runtime_error
)
518 if(e
.type
== SDL_JOYHATMOTION
) {
519 uint32_t axis
= static_cast<uint32_t>(e
.jhat
.which
) * 256 + static_cast<uint32_t>(e
.jhat
.hat
);
521 if(e
.jhat
.value
& SDL_HAT_UP
) v
|= 1;
522 if(e
.jhat
.value
& SDL_HAT_RIGHT
) v
|= 2;
523 if(e
.jhat
.value
& SDL_HAT_DOWN
) v
|= 4;
524 if(e
.jhat
.value
& SDL_HAT_LEFT
) v
|= 8;
525 unsigned ov
= hat_current_state
[axis
];
527 bool polarity
= false;
530 k
.device
= SDL_DEV_NONE
;
531 return translated_event(k
, false);
533 hat_transition(ov
, v
, polarity
, more
);
534 hat_current_state
[axis
] = ov
;
537 k
.device
= SDL_DEV_JOYHAT
;
539 return translated_event(k
, polarity
, more
);
540 } else if(e
.type
== SDL_JOYAXISMOTION
) {
541 uint32_t axis
= static_cast<uint32_t>(e
.jaxis
.which
) * 256 + static_cast<uint32_t>(e
.jaxis
.axis
);
542 int v
= calibrations
[axis
].digital(e
.jaxis
.value
);
543 int ov
= axis_current_state
[axis
];
545 bool polarity
= false;
548 k
.device
= SDL_DEV_NONE
;
549 return translated_event(k
, false);
550 } else if(ov
== -1) {
555 axis
|= ((v
== 1) ? 0x10000 : 0);
562 k
.device
= SDL_DEV_JOYAXIS
;
564 return translated_event(k
, polarity
, more
);
565 } else if(e
.type
== SDL_JOYBUTTONUP
|| e
.type
== SDL_JOYBUTTONDOWN
) {
567 k
.device
= SDL_DEV_JOYBUTTON
;
568 k
.joybutton
= static_cast<uint32_t>(e
.jbutton
.which
) * 256 + static_cast<uint32_t>(e
.jbutton
.button
);
569 return translated_event(k
, (e
.type
== SDL_JOYBUTTONDOWN
));
571 } else if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_KEYUP
) {
573 k
.device
= SDL_DEV_KEYBOARD
;
574 k
.keyboard
= e
.key
.keysym
;
575 return translated_event(k
, (e
.type
== SDL_KEYDOWN
));
578 k
.device
= SDL_DEV_NONE
;
579 return translated_event(k
, false);
583 bool keymapper_helper_sdl::internal_keysymbol::operator==(const internal_keysymbol
& k
) const
585 if(mode
== KEYTYPE_SCAN_SYMBOL
&& k
.mode
== KEYTYPE_SCAN_SYMBOL
)
586 return (scan
== k
.scan
|| symbolic
== k
.symbolic
);
587 if((mode
== KEYTYPE_SCAN_SYMBOL
&& k
.mode
== KEYTYPE_SCAN
) || (k
.mode
== KEYTYPE_SCAN_SYMBOL
&&
588 mode
== KEYTYPE_SCAN
) || (k
.mode
== KEYTYPE_SCAN
&& mode
== KEYTYPE_SCAN
))
589 return (scan
== k
.scan
);
590 if((mode
== KEYTYPE_SCAN_SYMBOL
&& k
.mode
== KEYTYPE_SYMBOL
) || (k
.mode
== KEYTYPE_SCAN_SYMBOL
&&
591 mode
== KEYTYPE_SYMBOL
) || (k
.mode
== KEYTYPE_SYMBOL
&& mode
== KEYTYPE_SYMBOL
))
592 return (symbolic
== k
.symbolic
);
593 if(mode
== KEYTYPE_JOYAXIS
&& k
.mode
== KEYTYPE_JOYAXIS
)
594 return (joyaxis
== k
.joyaxis
);
595 if(mode
== KEYTYPE_JOYHAT
&& k
.mode
== KEYTYPE_JOYHAT
) {
596 //Joystick hats are handled specially. Diffrent hats never compare equal.
597 if((joyhat
& 0xFFFF) != (k
.joyhat
& 0xFFFF))
599 //For the same hat, if there is overlap, then they compare equal.
600 return (((joyhat
& k
.joyhat
) >> 16) != 0);
605 unsigned keymapper_helper_sdl::mod_str(std::string mod
) throw(std::bad_alloc
, std::runtime_error
)
609 size_t split
= mod
.find_first_of(",");
611 if(split
< mod
.length()) {
612 omod
= mod
.substr(0, split
);
613 mod
= mod
.substr(split
+ 1);
615 throw std::runtime_error("Invalid modifier specification (trailing comma)");
620 ret
|= recognize_one_modifier(omod
);
625 unsigned keymapper_helper_sdl::mod_key(keymapper_helper_sdl::keysymbol key
) throw(std::bad_alloc
)
627 if(key
.device
!= SDL_DEV_KEYBOARD
)
629 struct modifier
* m
= modifiers_table
;
632 if(!m
->synthethic
&& (key
.keyboard
.mod
& m
->sdlvalue
))
639 keymapper_helper_sdl::internal_keysymbol
keymapper_helper_sdl::key_str(std::string key
) throw(std::bad_alloc
,
642 if(key
.length() > 8 && key
.substr(0, 8) == "joystick") {
643 const char* rest
= key
.c_str() + 8;
645 unsigned subtype
= 0;
647 unsigned long value
= strtoul(rest
, &end
, 10);
649 throw std::runtime_error("Bad joystick number '" + key
+ "'");
651 if(!strncmp(end
, "button", 6)) {
654 } else if(!strncmp(end
, "axis", 4)) {
657 } else if(!strncmp(end
, "hat", 3)) {
661 throw std::runtime_error("Bad joystick subtype '" + key
+ "'");
662 value
= strtoul(rest
, &end
, 10);
665 if(value
> 255 || *end
)
666 throw std::runtime_error("Bad joystick button '" + key
+ "'");
667 internal_keysymbol k
;
668 k
.mode
= KEYTYPE_JOYBUTTON
;
671 } else if(subtype
== 2) {
672 if(!strncmp(end
, "+", 1) && value
<= 255)
674 else if(!strncmp(end
, "-", 1) && value
<= 255)
677 throw std::runtime_error("Bad joystick axis '" + key
+ "'");
678 internal_keysymbol k
;
679 k
.mode
= KEYTYPE_JOYAXIS
;
682 } else if(subtype
== 3) {
683 if(!strncmp(end
, "n", 1) && value
<= 255)
685 else if(!strncmp(end
, "e", 1) && value
<= 255)
687 else if(!strncmp(end
, "s", 1) && value
<= 255)
689 else if(!strncmp(end
, "w", 1) && value
<= 255)
692 throw std::runtime_error("Bad joystick hat '" + key
+ "'");
693 internal_keysymbol k
;
694 k
.mode
= KEYTYPE_JOYHAT
;
698 } if(key
.length() > 3 && key
.substr(0, 3) == "key") {
699 const char* rest
= key
.c_str() + 3;
701 unsigned long value
= strtoul(rest
, &end
, 10);
702 if(*end
|| value
> 255)
703 throw std::runtime_error("Bad scancode keysymbol '" + key
+ "'");
704 internal_keysymbol k
;
705 k
.mode
= KEYTYPE_SCAN
;
710 struct key
* k
= keys_table
;
712 if(key
== std::string(k
->name
)) {
713 internal_keysymbol k2
;
714 k2
.mode
= KEYTYPE_SYMBOL
;
716 k2
.symbolic
= k
->symbol
;
721 throw std::runtime_error("Unknown key '" + key
+ "'");
724 keymapper_helper_sdl::internal_keysymbol
keymapper_helper_sdl::key_key(keymapper_helper_sdl::keysymbol key
)
725 throw(std::bad_alloc
)
727 internal_keysymbol k
;
728 if(key
.device
== SDL_DEV_KEYBOARD
) {
729 k
.mode
= KEYTYPE_SCAN_SYMBOL
;
730 k
.scan
= key
.keyboard
.scancode
;
731 k
.symbolic
= key
.keyboard
.sym
;
732 } else if(key
.device
== SDL_DEV_JOYAXIS
) {
733 k
.mode
= KEYTYPE_JOYAXIS
;
734 k
.joyaxis
= key
.joyaxis
;
735 } else if(key
.device
== SDL_DEV_JOYBUTTON
) {
736 k
.mode
= KEYTYPE_JOYBUTTON
;
737 k
.joybutton
= key
.joybutton
;
738 } else if(key
.device
== SDL_DEV_JOYHAT
) {
739 k
.mode
= KEYTYPE_JOYHAT
;
740 k
.joyhat
= key
.joyhat
;
742 k
.mode
= KEYTYPE_NONE
;
747 std::string
keymapper_helper_sdl::name_key(unsigned mod
, unsigned modmask
, struct internal_keysymbol key
)
748 throw(std::bad_alloc
)
750 std::string ret
= "";
751 if(mod
!= 0 || modmask
!= 0) {
752 struct modifier
* m
= modifiers_table
;
754 if((mod
& m
->asmodifier
) == m
->asmodifier
) {
755 ret
= ret
+ m
->name
+ ",";
759 ret
= ret
.substr(0, ret
.length() - 1);
763 if((modmask
& m
->asmodifier
) == m
->asmodifier
) {
764 ret
= ret
+ m
->name
+ ",";
768 ret
= ret
.substr(0, ret
.length() - 1);
771 if(key
.mode
== KEYTYPE_SCAN_SYMBOL
|| key
.mode
== KEYTYPE_SYMBOL
) {
772 struct key
* k
= keys_table
;
774 if(key
.symbolic
== k
->symbol
) {
780 } else if(key
.mode
== KEYTYPE_SCAN
) {
781 std::ostringstream x
;
783 ret
= ret
+ "key" + x
.str();
784 } else if(key
.mode
== KEYTYPE_JOYAXIS
) {
785 std::ostringstream x
;
786 x
<< "joystick" << ((key
.joyaxis
>> 8) & 0xFF) << "axis" << (key
.joyaxis
& 0xFF);
787 x
<< ((key
.joyaxis
& 0x10000) ? '+' : '-');
789 } else if(key
.mode
== KEYTYPE_JOYBUTTON
) {
790 std::ostringstream x
;
791 x
<< "joystick" << ((key
.joybutton
>> 8) & 0xFF) << "button" << (key
.joybutton
& 0xFF);
793 } else if(key
.mode
== KEYTYPE_JOYHAT
) {
794 std::ostringstream x
;
795 x
<< "joystick" << ((key
.joyhat
>> 8) & 0xFF) << "hat" << (key
.joyhat
& 0xFF);
796 if(key
.joyhat
& 0x10000)
798 if(key
.joyhat
& 0x40000)
800 if(key
.joyhat
& 0x20000)
802 if(key
.joyhat
& 0x80000)
806 ret
= ret
+ "<invalid>";
811 std::string
keymapper_helper_sdl::print_key_info(keymapper_helper_sdl::keysymbol key
) throw(std::bad_alloc
)
814 if(key
.device
== SDL_DEV_KEYBOARD
&& key
.keyboard
.mod
) {
815 ret
= ret
+ "Modifiers: ";
816 unsigned mod
= mod_key(key
);
817 struct modifier
* m
= modifiers_table
;
819 if((mod
& m
->asmodifier
) == m
->asmodifier
) {
820 ret
= ret
+ m
->name
+ " ";
826 if(key
.device
== SDL_DEV_KEYBOARD
) {
828 std::ostringstream x
;
829 x
<< static_cast<unsigned>(key
.keyboard
.scancode
);
830 ret
= ret
+ "Name: key" + x
.str() + "\n";
832 struct key
* k
= keys_table
;
834 if(key
.keyboard
.sym
== k
->symbol
) {
835 ret
= ret
+ "Name: " + k
->name
+ "\n";
840 } else if(key
.device
== SDL_DEV_JOYAXIS
) {
841 std::ostringstream x
;
842 x
<< "joystick" << ((key
.joyaxis
>> 8) & 0xFF) << "axis" << (key
.joyaxis
& 0xFF);
843 x
<< ((key
.joyaxis
& 0x10000) ? '+' : '-');
844 ret
+ ret
+ "Name: " + x
.str() + "\n";
845 } else if(key
.device
== SDL_DEV_JOYBUTTON
) {
846 std::ostringstream x
;
847 x
<< "joystick" << ((key
.joybutton
>> 8) & 0xFF) << "button" << (key
.joybutton
& 0xFF);
848 ret
+ ret
+ "Name: " + x
.str() + "\n";
849 } else if(key
.device
== SDL_DEV_JOYHAT
) {
850 std::ostringstream x
;
851 x
<< "joystick" << ((key
.joyhat
>> 8) & 0xFF) << "hat" << (key
.joyhat
& 0xFF);
852 if(key
.joyhat
& 0x10000)
854 if(key
.joyhat
& 0x20000)
856 if(key
.joyhat
& 0x40000)
858 if(key
.joyhat
& 0x80000)
860 ret
+ ret
+ "Name: " + x
.str() + "\n";
866 extern uint32_t fontdata
[];
871 uint32_t mouse_mask
= 0;
874 uint32_t vc_hscl
= 1;
875 uint32_t vc_vscl
= 1;
878 bool modal_return_flag
;
879 bool delayed_close_flag
;
881 std::string command_buf
;
882 bool command_overwrite
;
883 size_t command_cursor
;
884 unsigned old_screen_w
;
885 unsigned old_screen_h
;
887 std::map
<std::string
, std::string
> emustatus
;
888 std::map
<uint64_t, std::string
> messagebuffer
;
889 uint64_t messagebuffer_next_seq
;
890 uint64_t messagebuffer_first_seq
;
891 uint64_t messagebuffer_first_show
;
893 uint32_t maxmessages
;
894 std::list
<std::string
> commandhistory
;
895 std::list
<std::string
>::iterator commandhistory_itr
;
896 screen
* current_screen
;
897 keymapper
<keymapper_helper_sdl
> mapper
;
900 uint64_t last_ui_update
;
901 bool screen_is_dirty
;
902 std::ofstream system_log
;
903 SDL_keysym autorepeating_key
;
904 unsigned autorepeat_phase
= 0;
905 unsigned autorepeat_timecounter
= 0;
906 numeric_setting
autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
907 numeric_setting
autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
913 const size_t audiobuf_size
= 8192;
914 uint16_t audiobuf
[audiobuf_size
];
915 volatile size_t audiobuf_get
= 0;
916 volatile size_t audiobuf_put
= 0;
917 uint64_t sampledup_ctr
= 0;
918 uint64_t sampledup_inc
= 0;
919 uint64_t sampledup_mod
= 1;
920 Uint16 format
= AUDIO_S16SYS
;
922 bool sound_enabled
= true;
924 void calculate_sampledup(uint32_t real_rate
)
927 sampledup_inc
= 64081;
928 sampledup_mod
= 2 * real_rate
+ 64081;
931 void audiocb(void* dummy
, Uint8
* stream
, int len
)
933 static uint16_t lprev
= 32768;
934 static uint16_t rprev
= 32768;
936 lprev
= rprev
= 32768;
937 uint16_t bias
= (format
== AUDIO_S8
|| format
== AUDIO_S16LSB
|| format
== AUDIO_S16MSB
|| format
==
938 AUDIO_S16SYS
) ? 32768 : 0;
941 if(audiobuf_get
== audiobuf_put
) {
945 l
= lprev
= audiobuf
[audiobuf_get
++];
946 r
= rprev
= audiobuf
[audiobuf_get
++];
947 if(audiobuf_get
== audiobuf_size
)
952 if(format
== AUDIO_U8
|| format
== AUDIO_S8
) {
953 stream
[0] = (l
- bias
) >> 8;
955 stream
[1] = (r
- bias
) >> 8;
956 stream
+= (stereo
? 2 : 1);
957 len
-= (stereo
? 2 : 1);
958 } else if(format
== AUDIO_S16SYS
|| format
== AUDIO_U16SYS
) {
959 reinterpret_cast<uint16_t*>(stream
)[0] = (l
- bias
);
961 reinterpret_cast<int16_t*>(stream
)[1] = (r
- bias
);
962 stream
+= (stereo
? 4 : 2);
963 len
-= (stereo
? 4 : 2);
964 } else if(format
== AUDIO_S16LSB
|| format
== AUDIO_U16LSB
) {
965 stream
[0] = (l
- bias
);
966 stream
[1] = (l
- bias
) >> 8;
968 stream
[2] = (r
- bias
);
969 stream
[3] = (r
- bias
) >> 8;
971 stream
+= (stereo
? 4 : 2);
972 len
-= (stereo
? 4 : 2);
973 } else if(format
== AUDIO_S16MSB
|| format
== AUDIO_U16MSB
) {
974 stream
[1] = (l
- bias
);
975 stream
[0] = (l
- bias
) >> 8;
977 stream
[3] = (r
- bias
);
978 stream
[2] = (r
- bias
) >> 8;
980 stream
+= (stereo
? 4 : 2);
981 len
-= (stereo
? 4 : 2);
988 state
= WINSTATE_IDENTIFY
;
989 window::message("Press key to identify.");
990 window::notify_screen_update();
991 window::poll_inputs();
994 std::string
decode_string(std::string e
)
997 for(size_t i
= 0; i
< e
.length(); i
+= 4) {
999 uint32_t c1
= e
[i
] - 33;
1000 uint32_t c2
= e
[i
+ 1] - 33;
1001 uint32_t c3
= e
[i
+ 2] - 33;
1002 uint32_t c4
= e
[i
+ 3] - 33;
1003 uint32_t c
= (c1
<< 18) | (c2
<< 12) | (c3
<< 6) | c4
;
1006 } else if(c
< 0x800) {
1007 tmp
[0] = 0xC0 | (c
>> 6);
1008 tmp
[1] = 0x80 | (c
& 0x3F);
1009 } else if(c
< 0x10000) {
1010 tmp
[0] = 0xE0 | (c
>> 12);
1011 tmp
[1] = 0x80 | ((c
>> 6) & 0x3F);
1012 tmp
[2] = 0x80 | (c
& 0x3F);
1014 tmp
[0] = 0xF0 | (c
>> 18);
1015 tmp
[1] = 0x80 | ((c
>> 12) & 0x3F);
1016 tmp
[2] = 0x80 | ((c
>> 6) & 0x3F);
1017 tmp
[3] = 0x80 | (c
& 0x3F);
1024 void draw_rectangle(uint8_t* data
, uint32_t pitch
, uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
1025 uint32_t color
, uint32_t thickness
)
1027 for(uint32_t i
= x1
; i
< x2
; i
++)
1028 for(uint32_t j
= 0; j
< thickness
; j
++) {
1029 reinterpret_cast<uint32_t*>(data
+ pitch
* (y1
+ j
))[i
] = color
;
1030 reinterpret_cast<uint32_t*>(data
+ pitch
* (y2
- 1 - j
))[i
] = color
;
1032 for(uint32_t i
= y1
; i
< y2
; i
++)
1033 for(uint32_t j
= 0; j
< thickness
; j
++) {
1034 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x1
+ j
] = color
;
1035 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x2
- 1 - j
] = color
;
1039 std::vector
<uint32_t> decode_utf8(std::string s
)
1041 std::vector
<uint32_t> ret
;
1042 for(auto i
= s
.begin(); i
!= s
.end(); i
++) {
1043 uint32_t j
= static_cast<uint8_t>(*i
);
1049 uint32_t j2
= static_cast<uint8_t>(*(++i
));
1050 ret
.push_back((j
- 192) * 64 + (j2
- 128));
1051 } else if(j
< 240) {
1052 uint32_t j2
= static_cast<uint8_t>(*(++i
));
1053 uint32_t j3
= static_cast<uint8_t>(*(++i
));
1054 ret
.push_back((j
- 224) * 4096 + (j2
- 128) * 64 + (j3
- 128));
1056 uint32_t j2
= static_cast<uint8_t>(*(++i
));
1057 uint32_t j3
= static_cast<uint8_t>(*(++i
));
1058 uint32_t j4
= static_cast<uint8_t>(*(++i
));
1059 ret
.push_back((j
- 240) * 262144 + (j2
- 128) * 4096 + (j3
- 128) * 64 + (j4
- 128));
1065 void draw_string(uint8_t* base
, uint32_t pitch
, std::vector
<uint32_t> s
, uint32_t x
, uint32_t y
,
1066 uint32_t maxwidth
, uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
1068 base
+= y
* static_cast<size_t>(pitch
) + 4 * x
;
1072 for(auto si
= s
.begin(); si
!= s
.end(); si
++) {
1073 uint32_t old_x
= pos_x
;
1074 uint32_t curstart
= 16;
1075 if(c
== hilite_pos
&& hilite_mode
== 1)
1077 if(c
== hilite_pos
&& hilite_mode
== 2)
1079 auto g
= find_glyph(*si
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
1084 for(unsigned j
= 0; j
< 16; j
++) {
1085 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
1086 for(unsigned i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++)
1087 ptr
[old_x
+ i
] = (j
>= curstart
) ? 0xFFFFFFFFU
: 0;
1090 //Narrow/Wide glyph.
1091 for(unsigned j
= 0; j
< 16; j
++) {
1092 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
1093 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
1094 for(uint32_t i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++) {
1095 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
1097 b
^= (j
>= curstart
);
1098 ptr
[old_x
+ i
] = b
? 0xFFFFFFFFU
: 0;
1104 for(unsigned j
= 0; j
< 16; j
++) {
1105 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
1106 uint32_t curstart
= 16;
1107 if(c
== hilite_pos
&& hilite_mode
== 1)
1109 if(c
== hilite_pos
&& hilite_mode
== 2)
1111 for(uint32_t i
= pos_x
; i
< maxwidth
; i
++) {
1112 ptr
[i
] = ((i
- pos_x
) < 8 && j
>= curstart
) ? 0xFFFFFFFFU
: 0;
1117 void draw_string(uint8_t* base
, uint32_t pitch
, std::string s
, uint32_t x
, uint32_t y
, uint32_t maxwidth
,
1118 uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
1120 draw_string(base
, pitch
, decode_utf8(s
), x
, y
, maxwidth
, hilite_mode
, hilite_pos
);
1123 void draw_command(uint8_t* base
, uint32_t pitch
, std::string s
, size_t cursor
, uint32_t x
, uint32_t y
,
1124 uint32_t maxwidth
, bool overwrite
)
1126 //FIXME, scroll text if too long.
1127 uint32_t hilite_mode
= overwrite
? 2 : 1;
1128 auto s2
= decode_utf8(s
);
1129 draw_string(base
, pitch
, s2
, x
, y
, maxwidth
, hilite_mode
, cursor
);
1132 void draw_modal_dialog(SDL_Surface
* surf
, std::string msg
, bool confirm
)
1137 uint32_t height
= 0;
1139 msg
= msg
+ "\n\nHit Enter to confirm, Esc to cancel";
1141 msg
= msg
+ "\n\nHit Enter or Esc to dismiss";
1142 auto s2
= decode_utf8(msg
);
1143 for(auto i
= s2
.begin(); i
!= s2
.end(); i
++) {
1144 auto g
= find_glyph(*i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
1145 if(pos_x
+ g
.first
> width
)
1146 width
= static_cast<uint32_t>(pos_x
+ g
.first
);
1147 if(pos_y
+ 16 > static_cast<int32_t>(height
))
1148 height
= static_cast<uint32_t>(pos_y
+ 16);
1154 if(width
+ 12 >= static_cast<uint32_t>(surf
->w
)) {
1159 x1
= (surf
->w
- width
) / 2;
1162 if(height
+ 12 >= static_cast<uint32_t>(surf
->h
)) {
1167 y1
= (surf
->h
- height
) / 2;
1170 SDL_LockSurface(surf
);
1171 for(uint32_t j
= y1
- 6; j
< y2
+ 6; j
++)
1172 memset(reinterpret_cast<uint8_t*>(surf
->pixels
) + j
* surf
->pitch
+ 4 * (x1
- 6), 0,
1173 4 * (x2
- x1
+ 12));
1174 uint32_t bordercolor
= (128 << surf
->format
->Gshift
) | (255 << surf
->format
->Rshift
);
1175 draw_rectangle(reinterpret_cast<uint8_t*>(surf
->pixels
), surf
->pitch
, x1
- 4, y1
- 4, x2
+ 4, y2
+ 4,
1180 for(auto i
= s2
.begin(); i
!= s2
.end(); i
++) {
1181 uint32_t ox
= pos_x
;
1182 uint32_t oy
= pos_y
;
1183 auto g
= find_glyph(*i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
1184 if(static_cast<uint32_t>(pos_y
) > height
)
1186 uint8_t* base
= reinterpret_cast<uint8_t*>(surf
->pixels
) + (y1
+ oy
) * surf
->pitch
+
1189 //Narrow/Wide glyph.
1190 for(unsigned j
= 0; j
< 16; j
++) {
1191 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ surf
->pitch
* j
);
1192 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
1193 for(uint32_t i
= 0; i
< g
.first
&& (ox
+ i
) < width
; i
++) {
1194 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
1196 ptr
[i
] = b
? bordercolor
: 0;
1204 void do_keyboard_command_edit(SDL_keysym k
)
1206 //These are not command edit!
1207 if(k
.sym
== SDLK_ESCAPE
)
1209 if(k
.sym
== SDLK_RETURN
)
1211 if(k
.sym
== SDLK_KP_ENTER
)
1213 //Map keys a bit if numlock is off.
1214 if((k
.mod
& KMOD_NUM
) == 0) {
1216 case SDLK_KP0
: k
.sym
= SDLK_INSERT
; break;
1217 case SDLK_KP1
: k
.sym
= SDLK_END
; break;
1218 case SDLK_KP2
: k
.sym
= SDLK_DOWN
; break;
1219 case SDLK_KP3
: k
.sym
= SDLK_PAGEDOWN
; break;
1220 case SDLK_KP4
: k
.sym
= SDLK_LEFT
; break;
1221 case SDLK_KP5
: return;
1222 case SDLK_KP6
: k
.sym
= SDLK_RIGHT
; break;
1223 case SDLK_KP7
: k
.sym
= SDLK_HOME
; break;
1224 case SDLK_KP8
: k
.sym
= SDLK_UP
; break;
1225 case SDLK_KP9
: k
.sym
= SDLK_PAGEUP
; break;
1226 case SDLK_KP_PERIOD
: k
.sym
= SDLK_DELETE
; break;
1231 //Special editing operations.
1234 command_overwrite
= !command_overwrite
;
1235 window::notify_screen_update();
1238 command_cursor
= command_buf
.length();
1239 window::notify_screen_update();
1243 if(commandhistory_itr
!= commandhistory
.begin()) {
1244 commandhistory_itr
--;
1245 command_buf
= *commandhistory_itr
;
1246 if(command_cursor
> command_buf
.length())
1247 command_cursor
= command_buf
.length();
1249 window::notify_screen_update();
1252 command_cursor
= (command_cursor
> 0) ? (command_cursor
- 4) : 0;
1253 window::notify_screen_update();
1256 command_cursor
= (command_cursor
< command_buf
.length()) ? (command_cursor
+ 4) :
1257 command_buf
.length();
1258 window::notify_screen_update();
1262 window::notify_screen_update();
1266 auto tmp
= commandhistory_itr
;
1267 if(++tmp
!= commandhistory
.end()) {
1268 commandhistory_itr
++;
1269 command_buf
= *commandhistory_itr
;
1270 if(command_cursor
> command_buf
.length())
1271 command_cursor
= command_buf
.length();
1273 window::notify_screen_update();
1277 if(command_cursor
< command_buf
.length())
1278 command_buf
= command_buf
.substr(0, command_cursor
) +
1279 command_buf
.substr(command_cursor
+ 4);
1280 window::notify_screen_update();
1281 *commandhistory_itr
= command_buf
;
1283 case SDLK_BACKSPACE
:
1284 if(command_cursor
> 0) {
1285 command_buf
= command_buf
.substr(0, command_cursor
- 4) +
1286 command_buf
.substr(command_cursor
);
1287 command_cursor
-= 4;
1289 window::notify_screen_update();
1290 *commandhistory_itr
= command_buf
;
1296 //Not a special editing operation, insert/overwrite a character.
1297 uint32_t code
= k
.unicode
;
1300 uint8_t c1
= 33 + ((code
>> 18) & 0x3F);
1301 uint8_t c2
= 33 + ((code
>> 12) & 0x3F);
1302 uint8_t c3
= 33 + ((code
>> 6) & 0x3F);
1303 uint8_t c4
= 33 + (code
& 0x3F);
1304 if(command_overwrite
&& command_cursor
< command_buf
.length()) {
1305 command_buf
[command_cursor
] = c1
;
1306 command_buf
[command_cursor
+ 1] = c2
;
1307 command_buf
[command_cursor
+ 2] = c3
;
1308 command_buf
[command_cursor
+ 3] = c4
;
1309 command_cursor
+= 4;
1311 std::string foo
= " ";
1316 command_buf
= command_buf
.substr(0, command_cursor
) + foo
+ command_buf
.substr(command_cursor
);
1317 command_cursor
+= 4;
1319 *commandhistory_itr
= command_buf
;
1320 window::notify_screen_update();
1323 void do_event(SDL_Event
& e
) throw(std::bad_alloc
)
1325 alarm(WATCHDOG_TIMEOUT
);
1326 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
&& e
.key
.keysym
.mod
== (KMOD_LCTRL
|
1329 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
1331 window::notify_screen_update();
1335 if(e
.type
== SDL_ACTIVEEVENT
&& e
.active
.gain
&& e
.active
.state
== SDL_APPACTIVE
) {
1336 window::notify_screen_update();
1339 if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_KEYUP
)
1340 key
= e
.key
.keysym
.sym
;
1342 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_IDENTIFY
)
1344 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_MODAL
) {
1345 delayed_close_flag
= true;
1348 if(e
.type
== SDL_QUIT
) {
1349 command::invokeC("quit-emulator");
1350 state
= WINSTATE_NORMAL
;
1355 case WINSTATE_NORMAL
:
1356 if(e
.type
== SDL_MOUSEBUTTONDOWN
|| e
.type
== SDL_MOUSEBUTTONUP
) {
1357 int32_t xc
= e
.button
.x
;
1358 int32_t yc
= e
.button
.y
;
1359 xc
= (xc
- 6 - vc_xoffset
) / vc_hscl
;
1360 yc
= (yc
- 6 - vc_yoffset
) / vc_vscl
;
1361 if(e
.button
.button
== SDL_BUTTON_LEFT
) {
1362 if(e
.button
.state
== SDL_PRESSED
)
1367 if(e
.button
.button
== SDL_BUTTON_MIDDLE
) {
1368 if(e
.button
.state
== SDL_PRESSED
)
1373 if(e
.button
.button
== SDL_BUTTON_RIGHT
) {
1374 if(e
.button
.state
== SDL_PRESSED
)
1380 std::ostringstream x
;
1381 x
<< "mouse_button " << xc
<< " " << yc
<< " " << mouse_mask
;
1382 command::invokeC(x
.str());
1385 if(e
.type
== SDL_KEYDOWN
&& key
== SDLK_ESCAPE
)
1387 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
1388 state
= WINSTATE_COMMAND
;
1391 commandhistory
.push_front("");
1392 if(commandhistory
.size() > MAXHISTORY
)
1393 commandhistory
.pop_back();
1394 commandhistory_itr
= commandhistory
.begin();
1395 window::notify_screen_update();
1401 auto i
= keymapper_helper_sdl::translate_event(e
);
1403 if(i
.symbol
.device
!= SDL_DEV_NONE
)
1404 cmd
= mapper
.map(i
.symbol
, i
.polarity
);
1406 command::invokeC(cmd
);
1412 case WINSTATE_MODAL
:
1413 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
1414 state
= WINSTATE_NORMAL
;
1416 modal_return_flag
= true;
1418 window::notify_screen_update(true);
1421 if(e
.type
== SDL_KEYUP
&& (key
== SDLK_RETURN
|| key
== SDLK_KP_ENTER
)) {
1422 state
= WINSTATE_NORMAL
;
1423 modal_return_flag
= true;
1425 window::notify_screen_update(true);
1429 case WINSTATE_COMMAND
:
1430 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
) {
1431 state
= WINSTATE_NORMAL
;
1433 window::notify_screen_update();
1434 if(commandhistory
.front() == "")
1435 commandhistory
.pop_front();
1438 if(e
.type
== SDL_KEYUP
&& (e
.key
.keysym
.sym
== SDLK_RETURN
||
1439 e
.key
.keysym
.sym
== SDLK_KP_ENTER
)) {
1440 state
= WINSTATE_NORMAL
;
1441 if(commandhistory
.front() == "")
1442 commandhistory
.pop_front();
1443 command::invokeC(decode_string(command_buf
));
1445 window::notify_screen_update();
1446 autorepeat_phase
= 0;
1449 if(e
.type
== SDL_KEYDOWN
) {
1450 autorepeating_key
= e
.key
.keysym
;
1451 autorepeat_phase
= 1;
1452 autorepeat_timecounter
= 0;
1453 do_keyboard_command_edit(e
.key
.keysym
);
1454 } else if(e
.type
== SDL_KEYUP
) {
1455 autorepeat_phase
= 0;
1457 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
1458 autorepeat_timecounter
++;
1459 if(!autorepeat_phase
)
1461 unsigned timeout
= (autorepeat_phase
== 1) ? autorepeat_first
: autorepeat_subsequent
;
1462 if(autorepeat_timecounter
>= timeout
) {
1463 do_keyboard_command_edit(autorepeating_key
);
1464 autorepeat_timecounter
= 0;
1465 autorepeat_phase
= 2;
1469 case WINSTATE_IDENTIFY
:
1470 auto i
= keymapper_helper_sdl::translate_event(e
);
1471 if(!i
.polarity
&& i
.symbol
.device
!= SDL_DEV_NONE
)
1472 window::modal_message(keymapper_helper_sdl::print_key_info(i
.symbol
), false);
1480 signal(SIGALRM
, sigalrm_handler
);
1481 alarm(WATCHDOG_TIMEOUT
);
1482 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
1483 time_t curtime
= time(NULL
);
1484 struct tm
* tm
= localtime(&curtime
);
1486 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1487 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1488 system_log
<< "lsnes started at " << buffer
<< std::endl
;
1489 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1491 SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
| SDL_INIT_JOYSTICK
| SDL_INIT_TIMER
);
1492 SDL_EnableUNICODE(true);
1494 tid
= SDL_AddTimer(MIN_UPDATE_TIME
, timer_cb
, NULL
);
1496 state
= WINSTATE_NORMAL
;
1497 current_screen
= NULL
;
1498 pause_active
= false;
1500 command_overwrite
= false;
1503 modal_return_flag
= false;
1504 delayed_close_flag
= false;
1505 messagebuffer_next_seq
= 0;
1506 messagebuffer_first_seq
= 0;
1507 messagebuffer_first_show
= 0;
1508 console_mode
= false;
1509 maxmessages
= MAXMESSAGES
;
1511 notify_screen_update();
1512 std::string windowname
= "lsnes-" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
1513 SDL_WM_SetCaption(windowname
.c_str(), "lsnes");
1515 SDL_AudioSpec
* desired
= new SDL_AudioSpec();
1516 SDL_AudioSpec
* obtained
= new SDL_AudioSpec();
1518 desired
->freq
= 44100;
1519 desired
->format
= AUDIO_S16SYS
;
1520 desired
->channels
= 2;
1521 desired
->samples
= 8192;
1522 desired
->callback
= audiocb
;
1523 desired
->userdata
= NULL
;
1525 if(SDL_OpenAudio(desired
, obtained
) < 0) {
1526 message("Audio can't be initialized, audio playback disabled");
1530 //Fill the parameters.
1531 calculate_sampledup(obtained
->freq
);
1532 format
= obtained
->format
;
1533 stereo
= (obtained
->channels
== 2);
1540 time_t curtime
= time(NULL
);
1541 struct tm
* tm
= localtime(&curtime
);
1543 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1544 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1545 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
1546 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1549 SDL_RemoveTimer(tid
);
1555 bool window::modal_message(const std::string
& msg
, bool confirm
) throw(std::bad_alloc
)
1557 modconfirm
= confirm
;
1559 state
= WINSTATE_MODAL
;
1560 notify_screen_update();
1562 bool ret
= modconfirm
;
1563 if(delayed_close_flag
) {
1564 delayed_close_flag
= false;
1565 command::invokeC("quit-emulator");
1570 void window::message(const std::string
& msg
) throw(std::bad_alloc
)
1572 std::string msg2
= msg
;
1573 bool locked_mode
= (messagebuffer_next_seq
- messagebuffer_first_show
<= maxmessages
) ;
1575 size_t s
= msg2
.find_first_of("\n");
1577 if(s
>= msg2
.length()) {
1578 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
);
1579 system_log
<< forlog
<< std::endl
;
1582 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
.substr(0, s
));
1583 system_log
<< forlog
<< std::endl
;
1584 msg2
= msg2
.substr(s
+ 1);
1588 if(locked_mode
&& messagebuffer_first_show
+ maxmessages
< messagebuffer_next_seq
)
1589 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1591 while(messagebuffer
.size() > MSGHISTORY
) {
1592 messagebuffer
.erase(messagebuffer_first_seq
++);
1593 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1594 messagebuffer_first_show
= messagebuffer_first_seq
;
1596 notify_screen_update();
1599 void window::set_main_surface(screen
& scr
) throw()
1601 current_screen
= &scr
;
1602 notify_screen_update(true);
1605 void window::notify_screen_update(bool full
) throw()
1607 uint64_t curtime
= get_ticks_msec();
1608 if(!full
&& last_ui_update
< curtime
&& last_ui_update
+ MIN_UPDATE_TIME
> curtime
) {
1609 screen_is_dirty
= true;
1612 last_ui_update
= curtime
;
1613 screen_is_dirty
= false;
1616 std::ostringstream y
;
1617 y
<< get_framerate();
1618 emustatus
["FPS"] = y
.str();
1622 std::string command_showas
= decode_string(command_buf
);
1623 uint32_t screen_w
= 512;
1624 uint32_t screen_h
= 448;
1625 if(current_screen
&& current_screen
->width
>= 512 && current_screen
->height
>= 448) {
1626 screen_w
= current_screen
->width
;
1627 screen_h
= current_screen
->height
;
1629 uint32_t win_w
= ((screen_w
< 512) ? 512 : ((screen_w
+ 15) / 16 * 16)) + 278;
1630 uint32_t win_h
= screen_h
+ MAXMESSAGES
* 16 + 48;
1631 if(!hwsurf
|| static_cast<uint32_t>(hwsurf
->w
) != win_w
|| static_cast<uint32_t>(hwsurf
->h
) != win_h
||
1632 old_screen_w
!= screen_w
|| old_screen_h
!= screen_h
|| full
) {
1633 //Create/Resize the window.
1634 if(!hwsurf
|| static_cast<uint32_t>(hwsurf
->w
) != win_w
|| static_cast<uint32_t>(hwsurf
->h
) != win_h
) {
1635 SDL_Surface
* hwsurf2
= SDL_SetVideoMode(win_w
, win_h
, 32, SDL_SWSURFACE
| SDL_DOUBLEBUF
);
1637 //We are in too fucked up state to even print error as message.
1638 std::cout
<< "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl
;
1644 current_screen
->set_palette(hwsurf
->format
->Rshift
, hwsurf
->format
->Gshift
,
1645 hwsurf
->format
->Bshift
);
1646 //Blank the screen and draw borders.
1647 SDL_LockSurface(hwsurf
);
1648 memset(hwsurf
->pixels
, 0, win_h
* hwsurf
->pitch
);
1649 uint32_t bordercolor
= 255 << hwsurf
->format
->Gshift
;
1651 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, 2, win_w
- 2,
1652 win_h
- 28, bordercolor
, 2);
1653 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, win_h
- 26,
1654 win_w
- 2, win_h
- 2, bordercolor
, 2);
1656 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, 2, screen_w
+ 10,
1657 screen_h
+ 10, bordercolor
, 2);
1658 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, screen_w
+ 12, 2,
1659 screen_w
+ 276, screen_h
+ 10, bordercolor
, 2);
1660 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, screen_h
+ 12,
1661 win_w
- 2, screen_h
+ MAXMESSAGES
* 16 + 20, bordercolor
, 2);
1662 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2,
1663 screen_h
+ MAXMESSAGES
* 16 + 22, win_w
- 2, screen_h
+ MAXMESSAGES
* 16 + 46,
1666 SDL_UnlockSurface(hwsurf
);
1667 old_screen_w
= screen_w
;
1668 old_screen_h
= screen_h
;
1670 SDL_LockSurface(hwsurf
);
1672 if(current_screen
) {
1673 //Draw main screen (blanking background if needed).
1674 if(screen_w
< current_screen
->width
|| screen_h
< current_screen
->height
)
1675 for(uint32_t i
= 6; i
< screen_h
+ 6; i
++)
1676 memset(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + i
* hwsurf
->pitch
+ 24, 0,
1678 for(uint32_t i
= 0; i
< current_screen
->height
; i
++)
1679 memcpy(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + (i
+ 6) * hwsurf
->pitch
+ 24,
1680 reinterpret_cast<uint8_t*>(current_screen
->memory
) + current_screen
->pitch
* i
,
1681 4 * current_screen
->width
);
1684 for(uint32_t i
= 6; i
< screen_h
+ 6; i
++)
1685 memset(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + i
* hwsurf
->pitch
+ 24, 0,
1689 uint32_t status_x
= screen_w
+ 16;
1690 uint32_t status_y
= 6;
1691 for(auto i
= emustatus
.begin(); i
!= emustatus
.end(); i
++) {
1692 std::string msg
= i
->first
+ " " + i
->second
;
1693 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, msg
, status_x
, status_y
,
1697 while(status_y
- 6 < screen_h
/ 16 * 16) {
1698 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", status_x
, status_y
,
1706 message_y
= screen_h
+ 16;
1709 for(size_t j
= 0; j
< maxmessages
; j
++)
1711 std::ostringstream o
;
1712 if(messagebuffer_first_show
+ j
< messagebuffer_next_seq
)
1713 o
<< (messagebuffer_first_show
+ j
+ 1) << ": "
1714 << messagebuffer
[messagebuffer_first_show
+ j
];
1715 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, o
.str(), 6,
1716 message_y
+ 16 * j
, win_w
- 12);
1719 if(messagebuffer_next_seq
- messagebuffer_first_show
> maxmessages
)
1721 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "--More--", win_w
- 76,
1722 message_y
+ 16 * maxmessages
- 16, 64);
1727 uint32_t command_y
= win_h
- 22;
1729 if(state
== WINSTATE_COMMAND
)
1730 draw_command(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, command_showas
,
1731 command_cursor
/ 4, 6, command_y
, win_w
- 12, command_overwrite
);
1733 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", 6, command_y
,
1737 //Draw modal dialog.
1738 if(state
== WINSTATE_MODAL
)
1740 draw_modal_dialog(hwsurf
, modmsg
, modconfirm
);
1743 SDL_UnlockSurface(hwsurf
);
1747 void window::poll_inputs() throw(std::bad_alloc
)
1751 if(modal_return_flag
) {
1752 modal_return_flag
= false;
1755 if(state
== WINSTATE_NORMAL
&& !pause_active
&& !SDL_PollEvent(&e
))
1757 else if(state
== WINSTATE_NORMAL
&& !pause_active
)
1759 else if(SDL_WaitEvent(&e
))
1764 void window::bind(std::string mod
, std::string modmask
, std::string keyname
, std::string cmd
) throw(std::bad_alloc
,
1767 mapper
.bind(mod
, modmask
, keyname
, cmd
);
1769 message("Key " + keyname
+ " bound to '" + cmd
+ "'");
1771 message("Key " + mod
+ "/" + modmask
+ " " + keyname
+ " bound to '" + cmd
+ "'");
1774 void window::unbind(std::string mod
, std::string modmask
, std::string keyname
) throw(std::bad_alloc
,
1777 mapper
.unbind(mod
, modmask
, keyname
);
1779 message("Key " + keyname
+ " unbound");
1781 message("Key " + mod
+ "/" + modmask
+ " " + keyname
+ " unbound");
1784 std::map
<std::string
, std::string
>& window::get_emustatus() throw()
1789 void window::dumpbindings() throw(std::bad_alloc
)
1791 mapper
.dumpbindings();
1794 void window::paused(bool enable
) throw()
1796 pause_active
= enable
;
1797 notify_screen_update();
1800 void window::sound_enable(bool enable
) throw()
1802 sound_enabled
= enable
;
1803 SDL_PauseAudio(enable
? 0 : 1);
1808 function_ptr_command
<const std::string
&> enable_sound("enable-sound", "Enable/Disable sound",
1809 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
1810 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
1811 std::string s
= args
;
1812 if(s
== "on" || s
== "true" || s
== "1" || s
== "enable" || s
== "enabled")
1813 window::sound_enable(true);
1814 else if(s
== "off" || s
== "false" || s
== "0" || s
== "disable" || s
== "disabled")
1815 window::sound_enable(false);
1817 throw std::runtime_error("Bad sound setting");
1820 function_ptr_command
<> identify_key("identify-key", "Identify a key",
1821 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1822 []() throw(std::bad_alloc
, std::runtime_error
) {
1826 function_ptr_command
<> scroll_up("scroll-up", "Scroll messages a page up",
1827 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1828 []() throw(std::bad_alloc
, std::runtime_error
) {
1829 if(messagebuffer_first_show
> maxmessages
)
1830 messagebuffer_first_show
-= maxmessages
;
1832 messagebuffer_first_show
= 0;
1833 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1834 messagebuffer_first_show
= messagebuffer_first_seq
;
1835 window::notify_screen_update();
1838 function_ptr_command
<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1839 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1840 []() throw(std::bad_alloc
, std::runtime_error
) {
1841 messagebuffer_first_show
= messagebuffer_first_seq
;
1842 window::notify_screen_update();
1845 function_ptr_command
<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1846 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1847 []() throw(std::bad_alloc
, std::runtime_error
) {
1848 if(messagebuffer_next_seq
< maxmessages
)
1849 messagebuffer_first_show
= 0;
1851 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1852 window::notify_screen_update();
1855 function_ptr_command
<> scrolldown("scroll-down", "Scroll messages a page down",
1856 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1857 []() throw(std::bad_alloc
, std::runtime_error
) {
1858 messagebuffer_first_show
+= maxmessages
;
1859 if(messagebuffer_next_seq
< maxmessages
)
1860 messagebuffer_first_show
= 0;
1861 else if(messagebuffer_next_seq
< messagebuffer_first_show
+ maxmessages
)
1862 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1863 window::notify_screen_update();
1866 function_ptr_command
<> toggle_console("toggle-console", "Toggle console between small and full window",
1867 "Syntax: toggle-console\nToggles console between small and large.\n",
1868 []() throw(std::bad_alloc
, std::runtime_error
) {
1869 console_mode
= !console_mode
;
1871 maxmessages
= hwsurf
? (hwsurf
->h
- 38) / 16 : 36;
1873 maxmessages
= MAXMESSAGES
;
1874 if(messagebuffer_next_seq
< maxmessages
)
1875 messagebuffer_first_show
= 0;
1877 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1878 window::notify_screen_update(true);
1882 void window::wait_msec(uint64_t msec
) throw(std::bad_alloc
)
1884 wait_canceled
= false;
1885 uint64_t basetime
= get_ticks_msec();
1886 while(!wait_canceled
) {
1892 while(SDL_PollEvent(&e
))
1894 uint64_t passed
= get_ticks_msec() - basetime
;
1900 void window::fatal_error() throw()
1903 message("PANIC: Cannot continue, press ESC or close window to exit.");
1905 notify_screen_update(true);
1910 time_t curtime
= time(NULL
);
1911 struct tm
* tm
= localtime(&curtime
);
1913 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1914 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1915 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
1916 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1920 if(SDL_WaitEvent(&e
)) {
1921 if(e
.type
== SDL_QUIT
)
1923 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
)
1929 uint64_t get_ticks_msec() throw()
1931 static uint64_t tickbase
= 0;
1932 static Uint32 last_ticks
= 0;
1933 Uint32 cur_ticks
= SDL_GetTicks();
1934 if(last_ticks
> cur_ticks
)
1935 tickbase
+= 0x100000000ULL
;
1936 last_ticks
= cur_ticks
;
1937 return tickbase
+ cur_ticks
;
1940 void window::cancel_wait() throw()
1942 wait_canceled
= true;
1945 void window::play_audio_sample(uint16_t left
, uint16_t right
) throw()
1947 sampledup_ctr
+= sampledup_inc
;
1948 while(sampledup_ctr
< sampledup_mod
) {
1949 audiobuf
[audiobuf_put
++] = left
;
1950 audiobuf
[audiobuf_put
++] = right
;
1951 if(audiobuf_put
== audiobuf_size
)
1953 sampledup_ctr
+= sampledup_inc
;
1955 sampledup_ctr
-= sampledup_mod
;
1958 void window::set_window_compensation(uint32_t xoffset
, uint32_t yoffset
, uint32_t hscl
, uint32_t vscl
)
1960 vc_xoffset
= xoffset
;
1961 vc_yoffset
= yoffset
;