6 #include "settings.hpp"
10 #include "keymapper.hpp"
11 #include "framerate.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
41 void sigalrm_handler(int s
)
46 Uint32
timer_cb(Uint32 interval
, void* param
)
49 e
.type
= SDL_USEREVENT
;
60 } modifiers_table
[] = {
62 { "lctrl", "ctrl", KMOD_LCTRL
},
63 { "rctrl", "ctrl", KMOD_RCTRL
},
65 { "lalt", "alt", KMOD_LALT
},
66 { "ralt", "alt", KMOD_RALT
},
68 { "lshift", "shift", KMOD_LSHIFT
},
69 { "rshift", "shift", KMOD_RSHIFT
},
71 { "lmeta", "meta", KMOD_LMETA
},
72 { "rmeta", "meta", KMOD_RMETA
},
73 { "num", NULL
, KMOD_NUM
},
74 { "caps", NULL
, KMOD_CAPS
},
75 { "mode", NULL
, KMOD_MODE
},
84 {"backspace", SDLK_BACKSPACE
},
86 {"clear", SDLK_CLEAR
},
87 {"return", SDLK_RETURN
},
88 {"pause", SDLK_PAUSE
},
89 {"escape", SDLK_ESCAPE
},
90 {"space", SDLK_SPACE
},
91 {"exclaim", SDLK_EXCLAIM
},
92 {"quotedbl", SDLK_QUOTEDBL
},
94 {"dollar", SDLK_DOLLAR
},
95 {"ampersand", SDLK_AMPERSAND
},
96 {"quote", SDLK_QUOTE
},
97 {"leftparen", SDLK_LEFTPAREN
},
98 {"rightparen", SDLK_RIGHTPAREN
},
99 {"asterisk", SDLK_ASTERISK
},
100 {"plus", SDLK_PLUS
},
101 {"comma", SDLK_COMMA
},
102 {"minus", SDLK_MINUS
},
103 {"period", SDLK_PERIOD
},
104 {"slash", SDLK_SLASH
},
115 {"colon", SDLK_COLON
},
116 {"semicolon", SDLK_SEMICOLON
},
117 {"less", SDLK_LESS
},
118 {"equals", SDLK_EQUALS
},
119 {"greater", SDLK_GREATER
},
120 {"question", SDLK_QUESTION
},
122 {"leftbracket", SDLK_LEFTBRACKET
},
123 {"backslash", SDLK_BACKSLASH
},
124 {"rightbracket", SDLK_RIGHTBRACKET
},
125 {"caret", SDLK_CARET
},
126 {"underscore", SDLK_UNDERSCORE
},
127 {"backquote", SDLK_BACKQUOTE
},
154 {"delete", SDLK_DELETE
},
155 {"world_0", SDLK_WORLD_0
},
156 {"world_1", SDLK_WORLD_1
},
157 {"world_2", SDLK_WORLD_2
},
158 {"world_3", SDLK_WORLD_3
},
159 {"world_4", SDLK_WORLD_4
},
160 {"world_5", SDLK_WORLD_5
},
161 {"world_6", SDLK_WORLD_6
},
162 {"world_7", SDLK_WORLD_7
},
163 {"world_8", SDLK_WORLD_8
},
164 {"world_9", SDLK_WORLD_9
},
165 {"world_10", SDLK_WORLD_10
},
166 {"world_11", SDLK_WORLD_11
},
167 {"world_12", SDLK_WORLD_12
},
168 {"world_13", SDLK_WORLD_13
},
169 {"world_14", SDLK_WORLD_14
},
170 {"world_15", SDLK_WORLD_15
},
171 {"world_16", SDLK_WORLD_16
},
172 {"world_17", SDLK_WORLD_17
},
173 {"world_18", SDLK_WORLD_18
},
174 {"world_19", SDLK_WORLD_19
},
175 {"world_20", SDLK_WORLD_20
},
176 {"world_21", SDLK_WORLD_21
},
177 {"world_22", SDLK_WORLD_22
},
178 {"world_23", SDLK_WORLD_23
},
179 {"world_24", SDLK_WORLD_24
},
180 {"world_25", SDLK_WORLD_25
},
181 {"world_26", SDLK_WORLD_26
},
182 {"world_27", SDLK_WORLD_27
},
183 {"world_28", SDLK_WORLD_28
},
184 {"world_29", SDLK_WORLD_29
},
185 {"world_30", SDLK_WORLD_30
},
186 {"world_31", SDLK_WORLD_31
},
187 {"world_32", SDLK_WORLD_32
},
188 {"world_33", SDLK_WORLD_33
},
189 {"world_34", SDLK_WORLD_34
},
190 {"world_35", SDLK_WORLD_35
},
191 {"world_36", SDLK_WORLD_36
},
192 {"world_37", SDLK_WORLD_37
},
193 {"world_38", SDLK_WORLD_38
},
194 {"world_39", SDLK_WORLD_39
},
195 {"world_40", SDLK_WORLD_40
},
196 {"world_41", SDLK_WORLD_41
},
197 {"world_42", SDLK_WORLD_42
},
198 {"world_43", SDLK_WORLD_43
},
199 {"world_44", SDLK_WORLD_44
},
200 {"world_45", SDLK_WORLD_45
},
201 {"world_46", SDLK_WORLD_46
},
202 {"world_47", SDLK_WORLD_47
},
203 {"world_48", SDLK_WORLD_48
},
204 {"world_49", SDLK_WORLD_49
},
205 {"world_50", SDLK_WORLD_50
},
206 {"world_51", SDLK_WORLD_51
},
207 {"world_52", SDLK_WORLD_52
},
208 {"world_53", SDLK_WORLD_53
},
209 {"world_54", SDLK_WORLD_54
},
210 {"world_55", SDLK_WORLD_55
},
211 {"world_56", SDLK_WORLD_56
},
212 {"world_57", SDLK_WORLD_57
},
213 {"world_58", SDLK_WORLD_58
},
214 {"world_59", SDLK_WORLD_59
},
215 {"world_60", SDLK_WORLD_60
},
216 {"world_61", SDLK_WORLD_61
},
217 {"world_62", SDLK_WORLD_62
},
218 {"world_63", SDLK_WORLD_63
},
219 {"world_64", SDLK_WORLD_64
},
220 {"world_65", SDLK_WORLD_65
},
221 {"world_66", SDLK_WORLD_66
},
222 {"world_67", SDLK_WORLD_67
},
223 {"world_68", SDLK_WORLD_68
},
224 {"world_69", SDLK_WORLD_69
},
225 {"world_70", SDLK_WORLD_70
},
226 {"world_71", SDLK_WORLD_71
},
227 {"world_72", SDLK_WORLD_72
},
228 {"world_73", SDLK_WORLD_73
},
229 {"world_74", SDLK_WORLD_74
},
230 {"world_75", SDLK_WORLD_75
},
231 {"world_76", SDLK_WORLD_76
},
232 {"world_77", SDLK_WORLD_77
},
233 {"world_78", SDLK_WORLD_78
},
234 {"world_79", SDLK_WORLD_79
},
235 {"world_80", SDLK_WORLD_80
},
236 {"world_81", SDLK_WORLD_81
},
237 {"world_82", SDLK_WORLD_82
},
238 {"world_83", SDLK_WORLD_83
},
239 {"world_84", SDLK_WORLD_84
},
240 {"world_85", SDLK_WORLD_85
},
241 {"world_86", SDLK_WORLD_86
},
242 {"world_87", SDLK_WORLD_87
},
243 {"world_88", SDLK_WORLD_88
},
244 {"world_89", SDLK_WORLD_89
},
245 {"world_90", SDLK_WORLD_90
},
246 {"world_91", SDLK_WORLD_91
},
247 {"world_92", SDLK_WORLD_92
},
248 {"world_93", SDLK_WORLD_93
},
249 {"world_94", SDLK_WORLD_94
},
250 {"world_95", SDLK_WORLD_95
},
261 {"kp_period", SDLK_KP_PERIOD
},
262 {"kp_divide", SDLK_KP_DIVIDE
},
263 {"kp_multiply", SDLK_KP_MULTIPLY
},
264 {"kp_minus", SDLK_KP_MINUS
},
265 {"kp_plus", SDLK_KP_PLUS
},
266 {"kp_enter", SDLK_KP_ENTER
},
267 {"kp_equals", SDLK_KP_EQUALS
},
269 {"down", SDLK_DOWN
},
270 {"right", SDLK_RIGHT
},
271 {"left", SDLK_LEFT
},
272 {"insert", SDLK_INSERT
},
273 {"home", SDLK_HOME
},
275 {"pageup", SDLK_PAGEUP
},
276 {"pagedown", SDLK_PAGEDOWN
},
292 {"numlock", SDLK_NUMLOCK
},
293 {"capslock", SDLK_CAPSLOCK
},
294 {"scrollock", SDLK_SCROLLOCK
},
295 {"rshift", SDLK_RSHIFT
},
296 {"lshift", SDLK_LSHIFT
},
297 {"rctrl", SDLK_RCTRL
},
298 {"lctrl", SDLK_LCTRL
},
299 {"ralt", SDLK_RALT
},
300 {"lalt", SDLK_LALT
},
301 {"rmeta", SDLK_RMETA
},
302 {"lmeta", SDLK_LMETA
},
303 {"lsuper", SDLK_LSUPER
},
304 {"rsuper", SDLK_RSUPER
},
305 {"mode", SDLK_MODE
},
306 {"compose", SDLK_COMPOSE
},
307 {"help", SDLK_HELP
},
308 {"print", SDLK_PRINT
},
309 {"sysreq", SDLK_SYSREQ
},
310 {"break", SDLK_BREAK
},
311 {"menu", SDLK_MENU
},
312 {"power", SDLK_POWER
},
313 {"euro", SDLK_EURO
},
314 {"undo", SDLK_UNDO
},
318 std::map
<unsigned, modifier
*> supported_modifiers
;
319 std::map
<unsigned, keygroup
*> scancodekeys
;
320 std::map
<unsigned, keygroup
*> symbolkeys
;
321 std::map
<unsigned, keygroup
*> joyaxis
;
322 std::map
<unsigned, keygroup
*> joybutton
;
323 std::map
<unsigned, keygroup
*> joyhat
;
327 struct sdl_modifier
* m
= modifiers_table
;
331 m2
= new modifier(m
->name
, m
->linkname
);
333 m2
= new modifier(m
->name
);
335 supported_modifiers
[m
->sdlvalue
] = m2
;
338 struct sdl_key
* k
= keys_table
;
340 symbolkeys
[k
->symbol
] = new keygroup(k
->name
, keygroup::KT_KEY
);
343 for(unsigned i
= 0; i
< 256; i
++) {
344 std::ostringstream x
;
346 scancodekeys
[i
] = new keygroup(x
.str(), keygroup::KT_KEY
);
350 void init_joysticks()
352 int joysticks
= SDL_NumJoysticks();
354 window::out() << "No joysticks detected." << std::endl
;
356 window::out() << joysticks
<< " joystick(s) detected." << std::endl
;
357 for(int i
= 0; i
< joysticks
; i
++) {
358 SDL_Joystick
* j
= SDL_JoystickOpen(i
);
360 window::out() << "Joystick #" << i
<< ": Can't open!" << std::endl
;
363 window::out() << "Joystick #" << i
<< ": " << SDL_JoystickName(i
) << "("
364 << SDL_JoystickNumAxes(j
) << " axes, " << SDL_JoystickNumButtons(j
)
365 << " buttons, " << SDL_JoystickNumHats(j
) << " hats)." << std::endl
;
366 for(int k
= 0; k
< SDL_JoystickNumAxes(j
); k
++) {
367 unsigned num
= 256 * i
+ k
;
368 std::ostringstream x
;
369 x
<< "joystick" << i
<< "axis" << k
;
370 joyaxis
[num
] = new keygroup(x
.str(), keygroup::KT_AXIS_PAIR
);
372 for(int k
= 0; k
< SDL_JoystickNumButtons(j
); k
++) {
373 unsigned num
= 256 * i
+ k
;
374 std::ostringstream x
;
375 x
<< "joystick" << i
<< "button" << k
;
376 joybutton
[num
] = new keygroup(x
.str(), keygroup::KT_KEY
);
378 for(int k
= 0; k
< SDL_JoystickNumHats(j
); k
++) {
379 unsigned num
= 256 * i
+ k
;
380 std::ostringstream x
;
381 x
<< "joystick" << i
<< "hat" << k
;
382 joyhat
[num
] = new keygroup(x
.str(), keygroup::KT_HAT
);
388 struct identify_helper
: public keygroup::key_listener
390 void key_event(const modifier_set
& modifiers
, keygroup
& keygroup
, unsigned subkey
,
391 bool polarity
, const std::string
& name
)
394 _keys
= _keys
+ "Name: " + name
+ "\n";
398 return (_keys
!= "");
407 struct key_eater
: public keygroup::key_listener
409 void key_event(const modifier_set
& modifiers
, keygroup
& keygroup
, unsigned subkey
,
410 bool polarity
, const std::string
& name
)
416 void process_input_event(SDL_Event
* e
, bool identify
)
420 keygroup::set_exclusive_key_listener(&h
);
421 modifier_set modifiers
;
422 if(e
->type
== SDL_KEYDOWN
|| e
->type
== SDL_KEYUP
) {
423 SDL_keysym sym
= e
->key
.keysym
;
424 uint8_t scancode
= sym
.scancode
;
425 unsigned symbol
= sym
.sym
;
426 for(auto k
= supported_modifiers
.begin(); k
!= supported_modifiers
.end(); ++k
)
427 if(sym
.mod
& k
->first
)
428 modifiers
.add(*k
->second
);
429 scancodekeys
[scancode
]->set_position((e
->type
== SDL_KEYDOWN
) ? 1 : 0, modifiers
);
430 if(symbolkeys
.count(symbol
))
431 symbolkeys
[symbol
]->set_position((e
->type
== SDL_KEYDOWN
) ? 1 : 0, modifiers
);
432 } else if(e
->type
== SDL_JOYAXISMOTION
) {
433 unsigned num
= static_cast<unsigned>(e
->jaxis
.which
) * 256 +
434 static_cast<unsigned>(e
->jaxis
.axis
);
435 if(joyaxis
.count(num
))
436 joyaxis
[num
]->set_position(e
->jaxis
.value
, modifiers
);
437 } else if(e
->type
== SDL_JOYHATMOTION
) {
438 unsigned num
= static_cast<unsigned>(e
->jhat
.which
) * 256 +
439 static_cast<unsigned>(e
->jhat
.hat
);
441 if(e
->jhat
.value
& SDL_HAT_UP
)
443 if(e
->jhat
.value
& SDL_HAT_RIGHT
)
445 if(e
->jhat
.value
& SDL_HAT_DOWN
)
447 if(e
->jhat
.value
& SDL_HAT_LEFT
)
449 if(joyhat
.count(num
))
450 joyhat
[num
]->set_position(v
, modifiers
);
451 } else if(e
->type
== SDL_JOYBUTTONDOWN
|| e
->type
== SDL_JOYBUTTONUP
) {
452 unsigned num
= static_cast<unsigned>(e
->jbutton
.which
) * 256 +
453 static_cast<unsigned>(e
->jbutton
.button
);
454 if(joybutton
.count(num
))
455 joybutton
[num
]->set_position((e
->type
== SDL_JOYBUTTONDOWN
), modifiers
);
459 window::modal_message(h
.keys(), false);
460 keygroup::set_exclusive_key_listener(NULL
);
465 extern uint32_t fontdata
[];
470 uint32_t mouse_mask
= 0;
473 uint32_t vc_hscl
= 1;
474 uint32_t vc_vscl
= 1;
477 bool modal_return_flag
;
478 bool delayed_close_flag
;
480 std::string command_buf
;
481 bool command_overwrite
;
482 size_t command_cursor
;
483 unsigned old_screen_w
;
484 unsigned old_screen_h
;
486 std::map
<std::string
, std::string
> emustatus
;
487 std::map
<uint64_t, std::string
> messagebuffer
;
488 uint64_t messagebuffer_next_seq
;
489 uint64_t messagebuffer_first_seq
;
490 uint64_t messagebuffer_first_show
;
492 uint32_t maxmessages
;
493 std::list
<std::string
> commandhistory
;
494 std::list
<std::string
>::iterator commandhistory_itr
;
495 screen
* current_screen
;
498 uint64_t last_ui_update
;
499 bool screen_is_dirty
;
500 std::ofstream system_log
;
501 SDL_keysym autorepeating_key
;
502 unsigned autorepeat_phase
= 0;
503 unsigned autorepeat_timecounter
= 0;
504 numeric_setting
autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
505 numeric_setting
autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
508 void poll_inputs_internal() throw(std::bad_alloc
);
512 const size_t audiobuf_size
= 8192;
513 uint16_t audiobuf
[audiobuf_size
];
514 volatile size_t audiobuf_get
= 0;
515 volatile size_t audiobuf_put
= 0;
516 uint64_t sampledup_ctr
= 0;
517 uint64_t sampledup_inc
= 0;
518 uint64_t sampledup_mod
= 1;
519 Uint16 format
= AUDIO_S16SYS
;
521 bool sound_enabled
= true;
523 void calculate_sampledup(uint32_t real_rate
)
526 sampledup_inc
= 64081;
527 sampledup_mod
= 2 * real_rate
+ 64081;
530 void audiocb(void* dummy
, Uint8
* stream
, int len
)
532 static uint16_t lprev
= 32768;
533 static uint16_t rprev
= 32768;
535 lprev
= rprev
= 32768;
536 uint16_t bias
= (format
== AUDIO_S8
|| format
== AUDIO_S16LSB
|| format
== AUDIO_S16MSB
|| format
==
537 AUDIO_S16SYS
) ? 32768 : 0;
540 if(audiobuf_get
== audiobuf_put
) {
544 l
= lprev
= audiobuf
[audiobuf_get
++];
545 r
= rprev
= audiobuf
[audiobuf_get
++];
546 if(audiobuf_get
== audiobuf_size
)
551 if(format
== AUDIO_U8
|| format
== AUDIO_S8
) {
552 stream
[0] = (l
- bias
) >> 8;
554 stream
[1] = (r
- bias
) >> 8;
555 stream
+= (stereo
? 2 : 1);
556 len
-= (stereo
? 2 : 1);
557 } else if(format
== AUDIO_S16SYS
|| format
== AUDIO_U16SYS
) {
558 reinterpret_cast<uint16_t*>(stream
)[0] = (l
- bias
);
560 reinterpret_cast<int16_t*>(stream
)[1] = (r
- bias
);
561 stream
+= (stereo
? 4 : 2);
562 len
-= (stereo
? 4 : 2);
563 } else if(format
== AUDIO_S16LSB
|| format
== AUDIO_U16LSB
) {
564 stream
[0] = (l
- bias
);
565 stream
[1] = (l
- bias
) >> 8;
567 stream
[2] = (r
- bias
);
568 stream
[3] = (r
- bias
) >> 8;
570 stream
+= (stereo
? 4 : 2);
571 len
-= (stereo
? 4 : 2);
572 } else if(format
== AUDIO_S16MSB
|| format
== AUDIO_U16MSB
) {
573 stream
[1] = (l
- bias
);
574 stream
[0] = (l
- bias
) >> 8;
576 stream
[3] = (r
- bias
);
577 stream
[2] = (r
- bias
) >> 8;
579 stream
+= (stereo
? 4 : 2);
580 len
-= (stereo
? 4 : 2);
587 state
= WINSTATE_IDENTIFY
;
588 window::message("Press key to identify.");
589 window::notify_screen_update();
590 poll_inputs_internal();
593 std::string
decode_string(std::string e
)
596 for(size_t i
= 0; i
< e
.length(); i
+= 4) {
598 uint32_t c1
= e
[i
] - 33;
599 uint32_t c2
= e
[i
+ 1] - 33;
600 uint32_t c3
= e
[i
+ 2] - 33;
601 uint32_t c4
= e
[i
+ 3] - 33;
602 uint32_t c
= (c1
<< 18) | (c2
<< 12) | (c3
<< 6) | c4
;
605 } else if(c
< 0x800) {
606 tmp
[0] = 0xC0 | (c
>> 6);
607 tmp
[1] = 0x80 | (c
& 0x3F);
608 } else if(c
< 0x10000) {
609 tmp
[0] = 0xE0 | (c
>> 12);
610 tmp
[1] = 0x80 | ((c
>> 6) & 0x3F);
611 tmp
[2] = 0x80 | (c
& 0x3F);
613 tmp
[0] = 0xF0 | (c
>> 18);
614 tmp
[1] = 0x80 | ((c
>> 12) & 0x3F);
615 tmp
[2] = 0x80 | ((c
>> 6) & 0x3F);
616 tmp
[3] = 0x80 | (c
& 0x3F);
623 void draw_rectangle(uint8_t* data
, uint32_t pitch
, uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
624 uint32_t color
, uint32_t thickness
)
626 for(uint32_t i
= x1
; i
< x2
; i
++)
627 for(uint32_t j
= 0; j
< thickness
; j
++) {
628 reinterpret_cast<uint32_t*>(data
+ pitch
* (y1
+ j
))[i
] = color
;
629 reinterpret_cast<uint32_t*>(data
+ pitch
* (y2
- 1 - j
))[i
] = color
;
631 for(uint32_t i
= y1
; i
< y2
; i
++)
632 for(uint32_t j
= 0; j
< thickness
; j
++) {
633 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x1
+ j
] = color
;
634 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x2
- 1 - j
] = color
;
638 std::vector
<uint32_t> decode_utf8(std::string s
)
640 std::vector
<uint32_t> ret
;
641 for(auto i
= s
.begin(); i
!= s
.end(); i
++) {
642 uint32_t j
= static_cast<uint8_t>(*i
);
648 uint32_t j2
= static_cast<uint8_t>(*(++i
));
649 ret
.push_back((j
- 192) * 64 + (j2
- 128));
651 uint32_t j2
= static_cast<uint8_t>(*(++i
));
652 uint32_t j3
= static_cast<uint8_t>(*(++i
));
653 ret
.push_back((j
- 224) * 4096 + (j2
- 128) * 64 + (j3
- 128));
655 uint32_t j2
= static_cast<uint8_t>(*(++i
));
656 uint32_t j3
= static_cast<uint8_t>(*(++i
));
657 uint32_t j4
= static_cast<uint8_t>(*(++i
));
658 ret
.push_back((j
- 240) * 262144 + (j2
- 128) * 4096 + (j3
- 128) * 64 + (j4
- 128));
664 void draw_string(uint8_t* base
, uint32_t pitch
, std::vector
<uint32_t> s
, uint32_t x
, uint32_t y
,
665 uint32_t maxwidth
, uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
667 base
+= y
* static_cast<size_t>(pitch
) + 4 * x
;
671 for(auto si
= s
.begin(); si
!= s
.end(); si
++) {
672 uint32_t old_x
= pos_x
;
673 uint32_t curstart
= 16;
674 if(c
== hilite_pos
&& hilite_mode
== 1)
676 if(c
== hilite_pos
&& hilite_mode
== 2)
678 auto g
= find_glyph(*si
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
683 for(unsigned j
= 0; j
< 16; j
++) {
684 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
685 for(unsigned i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++)
686 ptr
[old_x
+ i
] = (j
>= curstart
) ? 0xFFFFFFFFU
: 0;
690 for(unsigned j
= 0; j
< 16; j
++) {
691 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
692 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
693 for(uint32_t i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++) {
694 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
696 b
^= (j
>= curstart
);
697 ptr
[old_x
+ i
] = b
? 0xFFFFFFFFU
: 0;
703 for(unsigned j
= 0; j
< 16; j
++) {
704 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
705 uint32_t curstart
= 16;
706 if(c
== hilite_pos
&& hilite_mode
== 1)
708 if(c
== hilite_pos
&& hilite_mode
== 2)
710 for(uint32_t i
= pos_x
; i
< maxwidth
; i
++) {
711 ptr
[i
] = ((i
- pos_x
) < 8 && j
>= curstart
) ? 0xFFFFFFFFU
: 0;
716 void draw_string(uint8_t* base
, uint32_t pitch
, std::string s
, uint32_t x
, uint32_t y
, uint32_t maxwidth
,
717 uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
719 draw_string(base
, pitch
, decode_utf8(s
), x
, y
, maxwidth
, hilite_mode
, hilite_pos
);
722 void draw_command(uint8_t* base
, uint32_t pitch
, std::string s
, size_t cursor
, uint32_t x
, uint32_t y
,
723 uint32_t maxwidth
, bool overwrite
)
725 //FIXME, scroll text if too long.
726 uint32_t hilite_mode
= overwrite
? 2 : 1;
727 auto s2
= decode_utf8(s
);
728 draw_string(base
, pitch
, s2
, x
, y
, maxwidth
, hilite_mode
, cursor
);
731 void draw_modal_dialog(SDL_Surface
* surf
, std::string msg
, bool confirm
)
738 msg
= msg
+ "\n\nHit Enter to confirm, Esc to cancel";
740 msg
= msg
+ "\n\nHit Enter or Esc to dismiss";
741 auto s2
= decode_utf8(msg
);
742 for(auto i
= s2
.begin(); i
!= s2
.end(); i
++) {
743 auto g
= find_glyph(*i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
744 if(pos_x
+ g
.first
> width
)
745 width
= static_cast<uint32_t>(pos_x
+ g
.first
);
746 if(pos_y
+ 16 > static_cast<int32_t>(height
))
747 height
= static_cast<uint32_t>(pos_y
+ 16);
753 if(width
+ 12 >= static_cast<uint32_t>(surf
->w
)) {
758 x1
= (surf
->w
- width
) / 2;
761 if(height
+ 12 >= static_cast<uint32_t>(surf
->h
)) {
766 y1
= (surf
->h
- height
) / 2;
769 SDL_LockSurface(surf
);
770 for(uint32_t j
= y1
- 6; j
< y2
+ 6; j
++)
771 memset(reinterpret_cast<uint8_t*>(surf
->pixels
) + j
* surf
->pitch
+ 4 * (x1
- 6), 0,
773 uint32_t bordercolor
= (128 << surf
->format
->Gshift
) | (255 << surf
->format
->Rshift
);
774 draw_rectangle(reinterpret_cast<uint8_t*>(surf
->pixels
), surf
->pitch
, x1
- 4, y1
- 4, x2
+ 4, y2
+ 4,
779 for(auto i
= s2
.begin(); i
!= s2
.end(); i
++) {
782 auto g
= find_glyph(*i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
783 if(static_cast<uint32_t>(pos_y
) > height
)
785 uint8_t* base
= reinterpret_cast<uint8_t*>(surf
->pixels
) + (y1
+ oy
) * surf
->pitch
+
789 for(unsigned j
= 0; j
< 16; j
++) {
790 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ surf
->pitch
* j
);
791 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
792 for(uint32_t i
= 0; i
< g
.first
&& (ox
+ i
) < width
; i
++) {
793 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
795 ptr
[i
] = b
? bordercolor
: 0;
803 void do_keyboard_command_edit(SDL_keysym k
)
805 //These are not command edit!
806 if(k
.sym
== SDLK_ESCAPE
)
808 if(k
.sym
== SDLK_RETURN
)
810 if(k
.sym
== SDLK_KP_ENTER
)
812 //Map keys a bit if numlock is off.
813 if((k
.mod
& KMOD_NUM
) == 0) {
815 case SDLK_KP0
: k
.sym
= SDLK_INSERT
; break;
816 case SDLK_KP1
: k
.sym
= SDLK_END
; break;
817 case SDLK_KP2
: k
.sym
= SDLK_DOWN
; break;
818 case SDLK_KP3
: k
.sym
= SDLK_PAGEDOWN
; break;
819 case SDLK_KP4
: k
.sym
= SDLK_LEFT
; break;
820 case SDLK_KP5
: return;
821 case SDLK_KP6
: k
.sym
= SDLK_RIGHT
; break;
822 case SDLK_KP7
: k
.sym
= SDLK_HOME
; break;
823 case SDLK_KP8
: k
.sym
= SDLK_UP
; break;
824 case SDLK_KP9
: k
.sym
= SDLK_PAGEUP
; break;
825 case SDLK_KP_PERIOD
: k
.sym
= SDLK_DELETE
; break;
830 //Special editing operations.
833 command_overwrite
= !command_overwrite
;
834 window::notify_screen_update();
837 command_cursor
= command_buf
.length();
838 window::notify_screen_update();
842 if(commandhistory_itr
!= commandhistory
.begin()) {
843 commandhistory_itr
--;
844 command_buf
= *commandhistory_itr
;
845 if(command_cursor
> command_buf
.length())
846 command_cursor
= command_buf
.length();
848 window::notify_screen_update();
851 command_cursor
= (command_cursor
> 0) ? (command_cursor
- 4) : 0;
852 window::notify_screen_update();
855 command_cursor
= (command_cursor
< command_buf
.length()) ? (command_cursor
+ 4) :
856 command_buf
.length();
857 window::notify_screen_update();
861 window::notify_screen_update();
865 auto tmp
= commandhistory_itr
;
866 if(++tmp
!= commandhistory
.end()) {
867 commandhistory_itr
++;
868 command_buf
= *commandhistory_itr
;
869 if(command_cursor
> command_buf
.length())
870 command_cursor
= command_buf
.length();
872 window::notify_screen_update();
876 if(command_cursor
< command_buf
.length())
877 command_buf
= command_buf
.substr(0, command_cursor
) +
878 command_buf
.substr(command_cursor
+ 4);
879 window::notify_screen_update();
880 *commandhistory_itr
= command_buf
;
883 if(command_cursor
> 0) {
884 command_buf
= command_buf
.substr(0, command_cursor
- 4) +
885 command_buf
.substr(command_cursor
);
888 window::notify_screen_update();
889 *commandhistory_itr
= command_buf
;
895 //Not a special editing operation, insert/overwrite a character.
896 uint32_t code
= k
.unicode
;
899 uint8_t c1
= 33 + ((code
>> 18) & 0x3F);
900 uint8_t c2
= 33 + ((code
>> 12) & 0x3F);
901 uint8_t c3
= 33 + ((code
>> 6) & 0x3F);
902 uint8_t c4
= 33 + (code
& 0x3F);
903 if(command_overwrite
&& command_cursor
< command_buf
.length()) {
904 command_buf
[command_cursor
] = c1
;
905 command_buf
[command_cursor
+ 1] = c2
;
906 command_buf
[command_cursor
+ 2] = c3
;
907 command_buf
[command_cursor
+ 3] = c4
;
910 std::string foo
= " ";
915 command_buf
= command_buf
.substr(0, command_cursor
) + foo
+ command_buf
.substr(command_cursor
);
918 *commandhistory_itr
= command_buf
;
919 window::notify_screen_update();
922 void do_event(SDL_Event
& e
) throw(std::bad_alloc
)
924 alarm(WATCHDOG_TIMEOUT
);
925 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
&& e
.key
.keysym
.mod
== (KMOD_LCTRL
|
928 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
930 window::notify_screen_update();
934 if(e
.type
== SDL_ACTIVEEVENT
&& e
.active
.gain
&& e
.active
.state
== SDL_APPACTIVE
) {
935 window::notify_screen_update();
938 if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_KEYUP
)
939 key
= e
.key
.keysym
.sym
;
941 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_IDENTIFY
)
943 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_MODAL
) {
944 delayed_close_flag
= true;
947 if(e
.type
== SDL_QUIT
) {
948 command::invokeC("quit-emulator");
949 state
= WINSTATE_NORMAL
;
954 case WINSTATE_NORMAL
:
955 if(e
.type
== SDL_MOUSEBUTTONDOWN
|| e
.type
== SDL_MOUSEBUTTONUP
) {
956 int32_t xc
= e
.button
.x
;
957 int32_t yc
= e
.button
.y
;
958 xc
= (xc
- 6 - vc_xoffset
) / vc_hscl
;
959 yc
= (yc
- 6 - vc_yoffset
) / vc_vscl
;
960 if(e
.button
.button
== SDL_BUTTON_LEFT
) {
961 if(e
.button
.state
== SDL_PRESSED
)
966 if(e
.button
.button
== SDL_BUTTON_MIDDLE
) {
967 if(e
.button
.state
== SDL_PRESSED
)
972 if(e
.button
.button
== SDL_BUTTON_RIGHT
) {
973 if(e
.button
.state
== SDL_PRESSED
)
979 std::ostringstream x
;
980 x
<< "mouse_button " << xc
<< " " << yc
<< " " << mouse_mask
;
981 command::invokeC(x
.str());
984 if(e
.type
== SDL_KEYDOWN
&& key
== SDLK_ESCAPE
)
986 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
987 state
= WINSTATE_COMMAND
;
990 commandhistory
.push_front("");
991 if(commandhistory
.size() > MAXHISTORY
)
992 commandhistory
.pop_back();
993 commandhistory_itr
= commandhistory
.begin();
994 window::notify_screen_update();
995 poll_inputs_internal();
998 process_input_event(&e
, false);
1000 case WINSTATE_MODAL
:
1001 //Send the key and eat it (prevent input from getting confused).
1002 keygroup::set_exclusive_key_listener(&keyeater
);
1003 process_input_event(&e
, false),
1004 keygroup::set_exclusive_key_listener(NULL
);
1005 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
1006 state
= WINSTATE_NORMAL
;
1008 modal_return_flag
= true;
1010 window::notify_screen_update(true);
1013 if(e
.type
== SDL_KEYUP
&& (key
== SDLK_RETURN
|| key
== SDLK_KP_ENTER
)) {
1014 state
= WINSTATE_NORMAL
;
1015 modal_return_flag
= true;
1017 window::notify_screen_update(true);
1021 case WINSTATE_COMMAND
:
1022 //Send the key and eat it (prevent input from getting confused).
1023 keygroup::set_exclusive_key_listener(&keyeater
);
1024 process_input_event(&e
, false),
1025 keygroup::set_exclusive_key_listener(NULL
);
1026 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
) {
1027 state
= WINSTATE_NORMAL
;
1029 window::notify_screen_update();
1030 if(commandhistory
.front() == "")
1031 commandhistory
.pop_front();
1034 if(e
.type
== SDL_KEYUP
&& (e
.key
.keysym
.sym
== SDLK_RETURN
||
1035 e
.key
.keysym
.sym
== SDLK_KP_ENTER
)) {
1036 state
= WINSTATE_NORMAL
;
1037 if(commandhistory
.front() == "")
1038 commandhistory
.pop_front();
1039 command::invokeC(decode_string(command_buf
));
1041 window::notify_screen_update();
1042 autorepeat_phase
= 0;
1045 if(e
.type
== SDL_KEYDOWN
) {
1046 autorepeating_key
= e
.key
.keysym
;
1047 autorepeat_phase
= 1;
1048 autorepeat_timecounter
= 0;
1049 do_keyboard_command_edit(e
.key
.keysym
);
1050 } else if(e
.type
== SDL_KEYUP
) {
1051 autorepeat_phase
= 0;
1053 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
1054 autorepeat_timecounter
++;
1055 if(!autorepeat_phase
)
1057 unsigned timeout
= (autorepeat_phase
== 1) ? autorepeat_first
: autorepeat_subsequent
;
1058 if(autorepeat_timecounter
>= timeout
) {
1059 do_keyboard_command_edit(autorepeating_key
);
1060 autorepeat_timecounter
= 0;
1061 autorepeat_phase
= 2;
1065 case WINSTATE_IDENTIFY
:
1066 process_input_event(&e
, true);
1074 signal(SIGALRM
, sigalrm_handler
);
1075 alarm(WATCHDOG_TIMEOUT
);
1077 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
1078 time_t curtime
= time(NULL
);
1079 struct tm
* tm
= localtime(&curtime
);
1081 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1082 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1083 system_log
<< "lsnes started at " << buffer
<< std::endl
;
1084 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1086 SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
| SDL_INIT_JOYSTICK
| SDL_INIT_TIMER
);
1087 SDL_EnableUNICODE(true);
1089 tid
= SDL_AddTimer(MIN_UPDATE_TIME
, timer_cb
, NULL
);
1091 state
= WINSTATE_NORMAL
;
1092 current_screen
= NULL
;
1093 pause_active
= false;
1095 command_overwrite
= false;
1098 modal_return_flag
= false;
1099 delayed_close_flag
= false;
1100 messagebuffer_next_seq
= 0;
1101 messagebuffer_first_seq
= 0;
1102 messagebuffer_first_show
= 0;
1103 console_mode
= false;
1104 maxmessages
= MAXMESSAGES
;
1106 notify_screen_update();
1107 std::string windowname
= "lsnes-" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
1108 SDL_WM_SetCaption(windowname
.c_str(), "lsnes");
1112 SDL_AudioSpec
* desired
= new SDL_AudioSpec();
1113 SDL_AudioSpec
* obtained
= new SDL_AudioSpec();
1115 desired
->freq
= 44100;
1116 desired
->format
= AUDIO_S16SYS
;
1117 desired
->channels
= 2;
1118 desired
->samples
= 8192;
1119 desired
->callback
= audiocb
;
1120 desired
->userdata
= NULL
;
1122 if(SDL_OpenAudio(desired
, obtained
) < 0) {
1123 message("Audio can't be initialized, audio playback disabled");
1127 //Fill the parameters.
1128 calculate_sampledup(obtained
->freq
);
1129 format
= obtained
->format
;
1130 stereo
= (obtained
->channels
== 2);
1137 time_t curtime
= time(NULL
);
1138 struct tm
* tm
= localtime(&curtime
);
1140 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1141 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1142 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
1143 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1146 SDL_RemoveTimer(tid
);
1152 bool window::modal_message(const std::string
& msg
, bool confirm
) throw(std::bad_alloc
)
1154 modconfirm
= confirm
;
1156 state
= WINSTATE_MODAL
;
1157 notify_screen_update();
1158 poll_inputs_internal();
1159 bool ret
= modconfirm
;
1160 if(delayed_close_flag
) {
1161 delayed_close_flag
= false;
1162 command::invokeC("quit-emulator");
1167 void window::message(const std::string
& msg
) throw(std::bad_alloc
)
1169 std::string msg2
= msg
;
1170 bool locked_mode
= (messagebuffer_next_seq
- messagebuffer_first_show
<= maxmessages
) ;
1172 size_t s
= msg2
.find_first_of("\n");
1174 if(s
>= msg2
.length()) {
1175 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
);
1176 system_log
<< forlog
<< std::endl
;
1179 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
.substr(0, s
));
1180 system_log
<< forlog
<< std::endl
;
1181 msg2
= msg2
.substr(s
+ 1);
1185 if(locked_mode
&& messagebuffer_first_show
+ maxmessages
< messagebuffer_next_seq
)
1186 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1188 while(messagebuffer
.size() > MSGHISTORY
) {
1189 messagebuffer
.erase(messagebuffer_first_seq
++);
1190 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1191 messagebuffer_first_show
= messagebuffer_first_seq
;
1193 notify_screen_update();
1196 void window::set_main_surface(screen
& scr
) throw()
1198 current_screen
= &scr
;
1199 notify_screen_update(true);
1202 void window::notify_screen_update(bool full
) throw()
1204 uint64_t curtime
= get_ticks_msec();
1205 if(!full
&& last_ui_update
< curtime
&& last_ui_update
+ MIN_UPDATE_TIME
> curtime
) {
1206 screen_is_dirty
= true;
1209 last_ui_update
= curtime
;
1210 screen_is_dirty
= false;
1213 std::ostringstream y
;
1214 y
<< get_framerate();
1215 emustatus
["FPS"] = y
.str();
1219 std::string command_showas
= decode_string(command_buf
);
1220 uint32_t screen_w
= 512;
1221 uint32_t screen_h
= 448;
1222 if(current_screen
&& current_screen
->width
>= 512 && current_screen
->height
>= 448) {
1223 screen_w
= current_screen
->width
;
1224 screen_h
= current_screen
->height
;
1226 uint32_t win_w
= ((screen_w
< 512) ? 512 : ((screen_w
+ 15) / 16 * 16)) + 278;
1227 uint32_t win_h
= screen_h
+ MAXMESSAGES
* 16 + 48;
1228 if(!hwsurf
|| static_cast<uint32_t>(hwsurf
->w
) != win_w
|| static_cast<uint32_t>(hwsurf
->h
) != win_h
||
1229 old_screen_w
!= screen_w
|| old_screen_h
!= screen_h
|| full
) {
1230 //Create/Resize the window.
1231 if(!hwsurf
|| static_cast<uint32_t>(hwsurf
->w
) != win_w
|| static_cast<uint32_t>(hwsurf
->h
) != win_h
) {
1232 SDL_Surface
* hwsurf2
= SDL_SetVideoMode(win_w
, win_h
, 32, SDL_SWSURFACE
| SDL_DOUBLEBUF
);
1234 //We are in too fucked up state to even print error as message.
1235 std::cout
<< "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl
;
1241 current_screen
->set_palette(hwsurf
->format
->Rshift
, hwsurf
->format
->Gshift
,
1242 hwsurf
->format
->Bshift
);
1243 //Blank the screen and draw borders.
1244 SDL_LockSurface(hwsurf
);
1245 memset(hwsurf
->pixels
, 0, win_h
* hwsurf
->pitch
);
1246 uint32_t bordercolor
= 255 << hwsurf
->format
->Gshift
;
1248 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, 2, win_w
- 2,
1249 win_h
- 28, bordercolor
, 2);
1250 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, win_h
- 26,
1251 win_w
- 2, win_h
- 2, bordercolor
, 2);
1253 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, 2, screen_w
+ 10,
1254 screen_h
+ 10, bordercolor
, 2);
1255 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, screen_w
+ 12, 2,
1256 screen_w
+ 276, screen_h
+ 10, bordercolor
, 2);
1257 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2, screen_h
+ 12,
1258 win_w
- 2, screen_h
+ MAXMESSAGES
* 16 + 20, bordercolor
, 2);
1259 draw_rectangle(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, 2,
1260 screen_h
+ MAXMESSAGES
* 16 + 22, win_w
- 2, screen_h
+ MAXMESSAGES
* 16 + 46,
1263 SDL_UnlockSurface(hwsurf
);
1264 old_screen_w
= screen_w
;
1265 old_screen_h
= screen_h
;
1267 SDL_LockSurface(hwsurf
);
1269 if(current_screen
) {
1270 //Draw main screen (blanking background if needed).
1271 if(screen_w
< current_screen
->width
|| screen_h
< current_screen
->height
)
1272 for(uint32_t i
= 6; i
< screen_h
+ 6; i
++)
1273 memset(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + i
* hwsurf
->pitch
+ 24, 0,
1275 for(uint32_t i
= 0; i
< current_screen
->height
; i
++)
1276 memcpy(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + (i
+ 6) * hwsurf
->pitch
+ 24,
1277 reinterpret_cast<uint8_t*>(current_screen
->memory
) + current_screen
->pitch
* i
,
1278 4 * current_screen
->width
);
1281 for(uint32_t i
= 6; i
< screen_h
+ 6; i
++)
1282 memset(reinterpret_cast<uint8_t*>(hwsurf
->pixels
) + i
* hwsurf
->pitch
+ 24, 0,
1286 uint32_t status_x
= screen_w
+ 16;
1287 uint32_t status_y
= 6;
1288 for(auto i
= emustatus
.begin(); i
!= emustatus
.end(); i
++) {
1289 std::string msg
= i
->first
+ " " + i
->second
;
1290 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, msg
, status_x
, status_y
,
1294 while(status_y
- 6 < screen_h
/ 16 * 16) {
1295 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", status_x
, status_y
,
1303 message_y
= screen_h
+ 16;
1306 for(size_t j
= 0; j
< maxmessages
; j
++)
1308 std::ostringstream o
;
1309 if(messagebuffer_first_show
+ j
< messagebuffer_next_seq
)
1310 o
<< (messagebuffer_first_show
+ j
+ 1) << ": "
1311 << messagebuffer
[messagebuffer_first_show
+ j
];
1312 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, o
.str(), 6,
1313 message_y
+ 16 * j
, win_w
- 12);
1316 if(messagebuffer_next_seq
- messagebuffer_first_show
> maxmessages
)
1318 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "--More--", win_w
- 76,
1319 message_y
+ 16 * maxmessages
- 16, 64);
1324 uint32_t command_y
= win_h
- 22;
1326 if(state
== WINSTATE_COMMAND
)
1327 draw_command(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, command_showas
,
1328 command_cursor
/ 4, 6, command_y
, win_w
- 12, command_overwrite
);
1330 draw_string(reinterpret_cast<uint8_t*>(hwsurf
->pixels
), hwsurf
->pitch
, "", 6, command_y
,
1334 //Draw modal dialog.
1335 if(state
== WINSTATE_MODAL
)
1337 draw_modal_dialog(hwsurf
, modmsg
, modconfirm
);
1340 SDL_UnlockSurface(hwsurf
);
1344 void poll_inputs_internal() throw(std::bad_alloc
)
1347 while(state
!= WINSTATE_NORMAL
) {
1348 if(SDL_WaitEvent(&e
))
1353 void window::poll_inputs() throw(std::bad_alloc
)
1357 assert(state
== WINSTATE_NORMAL
);
1358 if(!pause_active
&& !SDL_PollEvent(&e
))
1360 else if(!pause_active
)
1362 else if(SDL_WaitEvent(&e
))
1367 std::map
<std::string
, std::string
>& window::get_emustatus() throw()
1372 void window::paused(bool enable
) throw()
1374 pause_active
= enable
;
1375 notify_screen_update();
1378 void window::sound_enable(bool enable
) throw()
1380 sound_enabled
= enable
;
1381 SDL_PauseAudio(enable
? 0 : 1);
1386 function_ptr_command
<const std::string
&> enable_sound("enable-sound", "Enable/Disable sound",
1387 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
1388 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
1389 std::string s
= args
;
1390 if(s
== "on" || s
== "true" || s
== "1" || s
== "enable" || s
== "enabled")
1391 window::sound_enable(true);
1392 else if(s
== "off" || s
== "false" || s
== "0" || s
== "disable" || s
== "disabled")
1393 window::sound_enable(false);
1395 throw std::runtime_error("Bad sound setting");
1398 function_ptr_command
<> identify_key("identify-key", "Identify a key",
1399 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1400 []() throw(std::bad_alloc
, std::runtime_error
) {
1404 function_ptr_command
<> scroll_up("scroll-up", "Scroll messages a page up",
1405 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1406 []() throw(std::bad_alloc
, std::runtime_error
) {
1407 if(messagebuffer_first_show
> maxmessages
)
1408 messagebuffer_first_show
-= maxmessages
;
1410 messagebuffer_first_show
= 0;
1411 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1412 messagebuffer_first_show
= messagebuffer_first_seq
;
1413 window::notify_screen_update();
1416 function_ptr_command
<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1417 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1418 []() throw(std::bad_alloc
, std::runtime_error
) {
1419 messagebuffer_first_show
= messagebuffer_first_seq
;
1420 window::notify_screen_update();
1423 function_ptr_command
<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1424 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1425 []() throw(std::bad_alloc
, std::runtime_error
) {
1426 if(messagebuffer_next_seq
< maxmessages
)
1427 messagebuffer_first_show
= 0;
1429 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1430 window::notify_screen_update();
1433 function_ptr_command
<> scrolldown("scroll-down", "Scroll messages a page down",
1434 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1435 []() throw(std::bad_alloc
, std::runtime_error
) {
1436 messagebuffer_first_show
+= maxmessages
;
1437 if(messagebuffer_next_seq
< maxmessages
)
1438 messagebuffer_first_show
= 0;
1439 else if(messagebuffer_next_seq
< messagebuffer_first_show
+ maxmessages
)
1440 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1441 window::notify_screen_update();
1444 function_ptr_command
<> toggle_console("toggle-console", "Toggle console between small and full window",
1445 "Syntax: toggle-console\nToggles console between small and large.\n",
1446 []() throw(std::bad_alloc
, std::runtime_error
) {
1447 console_mode
= !console_mode
;
1449 maxmessages
= hwsurf
? (hwsurf
->h
- 38) / 16 : 36;
1451 maxmessages
= MAXMESSAGES
;
1452 if(messagebuffer_next_seq
< maxmessages
)
1453 messagebuffer_first_show
= 0;
1455 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1456 window::notify_screen_update(true);
1459 function_ptr_command
<tokensplitter
&> joystickmode("axismode", "Set joystick axis mode",
1460 "Syntax: axismode joystick<num>axis<axis> <mode>\nSet joystick axis mode.\n",
1461 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
1462 std::string axis
= t
;
1463 std::string mode
= t
;
1466 throw std::runtime_error("Expected exactly 2 parameters");
1467 keygroup
* tomod
= NULL
;
1468 for(auto i
= joyaxis
.begin(); i
!= joyaxis
.end(); ++i
)
1469 if(i
->second
->name() == axis
)
1472 throw std::runtime_error("Invalid axis");
1474 tomod
->change_type(keygroup::KT_AXIS_PAIR
);
1475 else if(mode
== "axis_inverse")
1476 tomod
->change_type(keygroup::KT_AXIS_PAIR_INVERSE
);
1477 else if(mode
== "pressure_0m")
1478 tomod
->change_type(keygroup::KT_PRESSURE_0M
);
1479 else if(mode
== "pressure_0p")
1480 tomod
->change_type(keygroup::KT_PRESSURE_0P
);
1481 else if(mode
== "pressure_m0")
1482 tomod
->change_type(keygroup::KT_PRESSURE_M0
);
1483 else if(mode
== "pressure_mp")
1484 tomod
->change_type(keygroup::KT_PRESSURE_MP
);
1485 else if(mode
== "pressure_p0")
1486 tomod
->change_type(keygroup::KT_PRESSURE_P0
);
1487 else if(mode
== "pressure_pm")
1488 tomod
->change_type(keygroup::KT_PRESSURE_PM
);
1489 else if(mode
== "disabled")
1490 tomod
->change_type(keygroup::KT_DISABLED
);
1492 throw std::runtime_error("Bad axis mode");
1497 void window::wait_msec(uint64_t msec
) throw(std::bad_alloc
)
1499 wait_canceled
= false;
1500 uint64_t basetime
= get_ticks_msec();
1501 while(!wait_canceled
) {
1507 while(SDL_PollEvent(&e
))
1509 uint64_t passed
= get_ticks_msec() - basetime
;
1515 void window::fatal_error() throw()
1518 message("PANIC: Cannot continue, press ESC or close window to exit.");
1520 notify_screen_update(true);
1525 time_t curtime
= time(NULL
);
1526 struct tm
* tm
= localtime(&curtime
);
1528 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1529 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1530 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
1531 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1535 if(SDL_WaitEvent(&e
)) {
1536 if(e
.type
== SDL_QUIT
)
1538 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
)
1544 uint64_t get_ticks_msec() throw()
1546 static uint64_t tickbase
= 0;
1547 static Uint32 last_ticks
= 0;
1548 Uint32 cur_ticks
= SDL_GetTicks();
1549 if(last_ticks
> cur_ticks
)
1550 tickbase
+= 0x100000000ULL
;
1551 last_ticks
= cur_ticks
;
1552 return tickbase
+ cur_ticks
;
1555 void window::cancel_wait() throw()
1557 wait_canceled
= true;
1560 void window::play_audio_sample(uint16_t left
, uint16_t right
) throw()
1562 sampledup_ctr
+= sampledup_inc
;
1563 while(sampledup_ctr
< sampledup_mod
) {
1564 audiobuf
[audiobuf_put
++] = left
;
1565 audiobuf
[audiobuf_put
++] = right
;
1566 if(audiobuf_put
== audiobuf_size
)
1568 sampledup_ctr
+= sampledup_inc
;
1570 sampledup_ctr
-= sampledup_mod
;
1573 void window::set_window_compensation(uint32_t xoffset
, uint32_t yoffset
, uint32_t hscl
, uint32_t vscl
)
1575 vc_xoffset
= xoffset
;
1576 vc_yoffset
= yoffset
;