4 #include "framerate.hpp"
7 #include "settings.hpp"
11 #include "keymapper.hpp"
12 #include "framerate.hpp"
17 #define WATCHDOG_TIMEOUT 15
19 #define MSGHISTORY 1000
20 #define MAXHISTORY 1000
27 // Limit the emulator to ~30fps.
28 #define MIN_UPDATE_TIME 33333
36 void sigalrm_handler(int s
)
41 Uint32
timer_cb(Uint32 interval
, void* param
)
44 e
.type
= SDL_USEREVENT
;
55 } modifiers_table
[] = {
57 { "lctrl", "ctrl", KMOD_LCTRL
},
58 { "rctrl", "ctrl", KMOD_RCTRL
},
60 { "lalt", "alt", KMOD_LALT
},
61 { "ralt", "alt", KMOD_RALT
},
63 { "lshift", "shift", KMOD_LSHIFT
},
64 { "rshift", "shift", KMOD_RSHIFT
},
66 { "lmeta", "meta", KMOD_LMETA
},
67 { "rmeta", "meta", KMOD_RMETA
},
68 { "num", NULL
, KMOD_NUM
},
69 { "caps", NULL
, KMOD_CAPS
},
70 { "mode", NULL
, KMOD_MODE
},
79 {"backspace", SDLK_BACKSPACE
},
81 {"clear", SDLK_CLEAR
},
82 {"return", SDLK_RETURN
},
83 {"pause", SDLK_PAUSE
},
84 {"escape", SDLK_ESCAPE
},
85 {"space", SDLK_SPACE
},
86 {"exclaim", SDLK_EXCLAIM
},
87 {"quotedbl", SDLK_QUOTEDBL
},
89 {"dollar", SDLK_DOLLAR
},
90 {"ampersand", SDLK_AMPERSAND
},
91 {"quote", SDLK_QUOTE
},
92 {"leftparen", SDLK_LEFTPAREN
},
93 {"rightparen", SDLK_RIGHTPAREN
},
94 {"asterisk", SDLK_ASTERISK
},
96 {"comma", SDLK_COMMA
},
97 {"minus", SDLK_MINUS
},
98 {"period", SDLK_PERIOD
},
99 {"slash", SDLK_SLASH
},
110 {"colon", SDLK_COLON
},
111 {"semicolon", SDLK_SEMICOLON
},
112 {"less", SDLK_LESS
},
113 {"equals", SDLK_EQUALS
},
114 {"greater", SDLK_GREATER
},
115 {"question", SDLK_QUESTION
},
117 {"leftbracket", SDLK_LEFTBRACKET
},
118 {"backslash", SDLK_BACKSLASH
},
119 {"rightbracket", SDLK_RIGHTBRACKET
},
120 {"caret", SDLK_CARET
},
121 {"underscore", SDLK_UNDERSCORE
},
122 {"backquote", SDLK_BACKQUOTE
},
149 {"delete", SDLK_DELETE
},
150 {"world_0", SDLK_WORLD_0
},
151 {"world_1", SDLK_WORLD_1
},
152 {"world_2", SDLK_WORLD_2
},
153 {"world_3", SDLK_WORLD_3
},
154 {"world_4", SDLK_WORLD_4
},
155 {"world_5", SDLK_WORLD_5
},
156 {"world_6", SDLK_WORLD_6
},
157 {"world_7", SDLK_WORLD_7
},
158 {"world_8", SDLK_WORLD_8
},
159 {"world_9", SDLK_WORLD_9
},
160 {"world_10", SDLK_WORLD_10
},
161 {"world_11", SDLK_WORLD_11
},
162 {"world_12", SDLK_WORLD_12
},
163 {"world_13", SDLK_WORLD_13
},
164 {"world_14", SDLK_WORLD_14
},
165 {"world_15", SDLK_WORLD_15
},
166 {"world_16", SDLK_WORLD_16
},
167 {"world_17", SDLK_WORLD_17
},
168 {"world_18", SDLK_WORLD_18
},
169 {"world_19", SDLK_WORLD_19
},
170 {"world_20", SDLK_WORLD_20
},
171 {"world_21", SDLK_WORLD_21
},
172 {"world_22", SDLK_WORLD_22
},
173 {"world_23", SDLK_WORLD_23
},
174 {"world_24", SDLK_WORLD_24
},
175 {"world_25", SDLK_WORLD_25
},
176 {"world_26", SDLK_WORLD_26
},
177 {"world_27", SDLK_WORLD_27
},
178 {"world_28", SDLK_WORLD_28
},
179 {"world_29", SDLK_WORLD_29
},
180 {"world_30", SDLK_WORLD_30
},
181 {"world_31", SDLK_WORLD_31
},
182 {"world_32", SDLK_WORLD_32
},
183 {"world_33", SDLK_WORLD_33
},
184 {"world_34", SDLK_WORLD_34
},
185 {"world_35", SDLK_WORLD_35
},
186 {"world_36", SDLK_WORLD_36
},
187 {"world_37", SDLK_WORLD_37
},
188 {"world_38", SDLK_WORLD_38
},
189 {"world_39", SDLK_WORLD_39
},
190 {"world_40", SDLK_WORLD_40
},
191 {"world_41", SDLK_WORLD_41
},
192 {"world_42", SDLK_WORLD_42
},
193 {"world_43", SDLK_WORLD_43
},
194 {"world_44", SDLK_WORLD_44
},
195 {"world_45", SDLK_WORLD_45
},
196 {"world_46", SDLK_WORLD_46
},
197 {"world_47", SDLK_WORLD_47
},
198 {"world_48", SDLK_WORLD_48
},
199 {"world_49", SDLK_WORLD_49
},
200 {"world_50", SDLK_WORLD_50
},
201 {"world_51", SDLK_WORLD_51
},
202 {"world_52", SDLK_WORLD_52
},
203 {"world_53", SDLK_WORLD_53
},
204 {"world_54", SDLK_WORLD_54
},
205 {"world_55", SDLK_WORLD_55
},
206 {"world_56", SDLK_WORLD_56
},
207 {"world_57", SDLK_WORLD_57
},
208 {"world_58", SDLK_WORLD_58
},
209 {"world_59", SDLK_WORLD_59
},
210 {"world_60", SDLK_WORLD_60
},
211 {"world_61", SDLK_WORLD_61
},
212 {"world_62", SDLK_WORLD_62
},
213 {"world_63", SDLK_WORLD_63
},
214 {"world_64", SDLK_WORLD_64
},
215 {"world_65", SDLK_WORLD_65
},
216 {"world_66", SDLK_WORLD_66
},
217 {"world_67", SDLK_WORLD_67
},
218 {"world_68", SDLK_WORLD_68
},
219 {"world_69", SDLK_WORLD_69
},
220 {"world_70", SDLK_WORLD_70
},
221 {"world_71", SDLK_WORLD_71
},
222 {"world_72", SDLK_WORLD_72
},
223 {"world_73", SDLK_WORLD_73
},
224 {"world_74", SDLK_WORLD_74
},
225 {"world_75", SDLK_WORLD_75
},
226 {"world_76", SDLK_WORLD_76
},
227 {"world_77", SDLK_WORLD_77
},
228 {"world_78", SDLK_WORLD_78
},
229 {"world_79", SDLK_WORLD_79
},
230 {"world_80", SDLK_WORLD_80
},
231 {"world_81", SDLK_WORLD_81
},
232 {"world_82", SDLK_WORLD_82
},
233 {"world_83", SDLK_WORLD_83
},
234 {"world_84", SDLK_WORLD_84
},
235 {"world_85", SDLK_WORLD_85
},
236 {"world_86", SDLK_WORLD_86
},
237 {"world_87", SDLK_WORLD_87
},
238 {"world_88", SDLK_WORLD_88
},
239 {"world_89", SDLK_WORLD_89
},
240 {"world_90", SDLK_WORLD_90
},
241 {"world_91", SDLK_WORLD_91
},
242 {"world_92", SDLK_WORLD_92
},
243 {"world_93", SDLK_WORLD_93
},
244 {"world_94", SDLK_WORLD_94
},
245 {"world_95", SDLK_WORLD_95
},
256 {"kp_period", SDLK_KP_PERIOD
},
257 {"kp_divide", SDLK_KP_DIVIDE
},
258 {"kp_multiply", SDLK_KP_MULTIPLY
},
259 {"kp_minus", SDLK_KP_MINUS
},
260 {"kp_plus", SDLK_KP_PLUS
},
261 {"kp_enter", SDLK_KP_ENTER
},
262 {"kp_equals", SDLK_KP_EQUALS
},
264 {"down", SDLK_DOWN
},
265 {"right", SDLK_RIGHT
},
266 {"left", SDLK_LEFT
},
267 {"insert", SDLK_INSERT
},
268 {"home", SDLK_HOME
},
270 {"pageup", SDLK_PAGEUP
},
271 {"pagedown", SDLK_PAGEDOWN
},
287 {"numlock", SDLK_NUMLOCK
},
288 {"capslock", SDLK_CAPSLOCK
},
289 {"scrollock", SDLK_SCROLLOCK
},
290 {"rshift", SDLK_RSHIFT
},
291 {"lshift", SDLK_LSHIFT
},
292 {"rctrl", SDLK_RCTRL
},
293 {"lctrl", SDLK_LCTRL
},
294 {"ralt", SDLK_RALT
},
295 {"lalt", SDLK_LALT
},
296 {"rmeta", SDLK_RMETA
},
297 {"lmeta", SDLK_LMETA
},
298 {"lsuper", SDLK_LSUPER
},
299 {"rsuper", SDLK_RSUPER
},
300 {"mode", SDLK_MODE
},
301 {"compose", SDLK_COMPOSE
},
302 {"help", SDLK_HELP
},
303 {"print", SDLK_PRINT
},
304 {"sysreq", SDLK_SYSREQ
},
305 {"break", SDLK_BREAK
},
306 {"menu", SDLK_MENU
},
307 {"power", SDLK_POWER
},
308 {"euro", SDLK_EURO
},
309 {"undo", SDLK_UNDO
},
313 std::map
<unsigned, modifier
*> supported_modifiers
;
314 std::map
<unsigned, keygroup
*> scancodekeys
;
315 std::map
<unsigned, keygroup
*> symbolkeys
;
316 std::map
<unsigned, keygroup
*> joyaxis
;
317 std::map
<unsigned, keygroup
*> joybutton
;
318 std::map
<unsigned, keygroup
*> joyhat
;
322 struct sdl_modifier
* m
= modifiers_table
;
326 m2
= new modifier(m
->name
, m
->linkname
);
328 m2
= new modifier(m
->name
);
330 supported_modifiers
[m
->sdlvalue
] = m2
;
333 struct sdl_key
* k
= keys_table
;
335 symbolkeys
[k
->symbol
] = new keygroup(k
->name
, keygroup::KT_KEY
);
338 for(unsigned i
= 0; i
< 256; i
++) {
339 std::ostringstream x
;
341 scancodekeys
[i
] = new keygroup(x
.str(), keygroup::KT_KEY
);
345 void init_joysticks()
347 int joysticks
= SDL_NumJoysticks();
349 window::out() << "No joysticks detected." << std::endl
;
351 window::out() << joysticks
<< " joystick(s) detected." << std::endl
;
352 for(int i
= 0; i
< joysticks
; i
++) {
353 SDL_Joystick
* j
= SDL_JoystickOpen(i
);
355 window::out() << "Joystick #" << i
<< ": Can't open!" << std::endl
;
358 window::out() << "Joystick #" << i
<< ": " << SDL_JoystickName(i
) << "("
359 << SDL_JoystickNumAxes(j
) << " axes, " << SDL_JoystickNumButtons(j
)
360 << " buttons, " << SDL_JoystickNumHats(j
) << " hats)." << std::endl
;
361 for(int k
= 0; k
< SDL_JoystickNumAxes(j
); k
++) {
362 unsigned num
= 256 * i
+ k
;
363 std::ostringstream x
;
364 x
<< "joystick" << i
<< "axis" << k
;
365 joyaxis
[num
] = new keygroup(x
.str(), keygroup::KT_AXIS_PAIR
);
367 for(int k
= 0; k
< SDL_JoystickNumButtons(j
); k
++) {
368 unsigned num
= 256 * i
+ k
;
369 std::ostringstream x
;
370 x
<< "joystick" << i
<< "button" << k
;
371 joybutton
[num
] = new keygroup(x
.str(), keygroup::KT_KEY
);
373 for(int k
= 0; k
< SDL_JoystickNumHats(j
); k
++) {
374 unsigned num
= 256 * i
+ k
;
375 std::ostringstream x
;
376 x
<< "joystick" << i
<< "hat" << k
;
377 joyhat
[num
] = new keygroup(x
.str(), keygroup::KT_HAT
);
383 struct identify_helper
: public keygroup::key_listener
385 void key_event(const modifier_set
& modifiers
, keygroup
& keygroup
, unsigned subkey
,
386 bool polarity
, const std::string
& name
)
389 _keys
= _keys
+ "Name: " + name
+ "\n";
393 return (_keys
!= "");
402 struct key_eater
: public keygroup::key_listener
404 void key_event(const modifier_set
& modifiers
, keygroup
& keygroup
, unsigned subkey
,
405 bool polarity
, const std::string
& name
)
411 void process_input_event(SDL_Event
* e
, bool identify
)
415 keygroup::set_exclusive_key_listener(&h
);
416 modifier_set modifiers
;
417 if(e
->type
== SDL_KEYDOWN
|| e
->type
== SDL_KEYUP
) {
418 SDL_keysym sym
= e
->key
.keysym
;
419 uint8_t scancode
= sym
.scancode
;
420 unsigned symbol
= sym
.sym
;
421 for(auto k
: supported_modifiers
)
422 if(sym
.mod
& k
.first
)
423 modifiers
.add(*k
.second
);
424 scancodekeys
[scancode
]->set_position((e
->type
== SDL_KEYDOWN
) ? 1 : 0, modifiers
);
425 if(symbolkeys
.count(symbol
))
426 symbolkeys
[symbol
]->set_position((e
->type
== SDL_KEYDOWN
) ? 1 : 0, modifiers
);
427 } else if(e
->type
== SDL_JOYAXISMOTION
) {
428 unsigned num
= static_cast<unsigned>(e
->jaxis
.which
) * 256 +
429 static_cast<unsigned>(e
->jaxis
.axis
);
430 if(joyaxis
.count(num
))
431 joyaxis
[num
]->set_position(e
->jaxis
.value
, modifiers
);
432 } else if(e
->type
== SDL_JOYHATMOTION
) {
433 unsigned num
= static_cast<unsigned>(e
->jhat
.which
) * 256 +
434 static_cast<unsigned>(e
->jhat
.hat
);
436 if(e
->jhat
.value
& SDL_HAT_UP
)
438 if(e
->jhat
.value
& SDL_HAT_RIGHT
)
440 if(e
->jhat
.value
& SDL_HAT_DOWN
)
442 if(e
->jhat
.value
& SDL_HAT_LEFT
)
444 if(joyhat
.count(num
))
445 joyhat
[num
]->set_position(v
, modifiers
);
446 } else if(e
->type
== SDL_JOYBUTTONDOWN
|| e
->type
== SDL_JOYBUTTONUP
) {
447 unsigned num
= static_cast<unsigned>(e
->jbutton
.which
) * 256 +
448 static_cast<unsigned>(e
->jbutton
.button
);
449 if(joybutton
.count(num
))
450 joybutton
[num
]->set_position((e
->type
== SDL_JOYBUTTONDOWN
), modifiers
);
454 window::modal_message(h
.keys(), false);
455 keygroup::set_exclusive_key_listener(NULL
);
460 extern uint32_t fontdata
[];
464 bool SDL_initialized
= false;
465 uint32_t mouse_mask
= 0;
468 uint32_t vc_hscl
= 1;
469 uint32_t vc_vscl
= 1;
472 bool modal_return_flag
;
473 bool delayed_close_flag
;
475 std::string command_buf
;
476 bool command_overwrite
;
477 size_t command_cursor
;
478 unsigned old_screen_w
;
479 unsigned old_screen_h
;
481 std::map
<std::string
, std::string
> emustatus
;
482 std::map
<uint64_t, std::string
> messagebuffer
;
483 uint64_t messagebuffer_next_seq
;
484 uint64_t messagebuffer_first_seq
;
485 uint64_t messagebuffer_first_show
;
487 uint32_t maxmessages
;
488 std::list
<std::string
> commandhistory
;
489 std::list
<std::string
>::iterator commandhistory_itr
;
490 screen
* current_screen
;
493 std::pair
<uint32_t, uint32_t> current_windowsize
;
495 uint64_t last_ui_update
;
496 bool screen_is_dirty
;
497 std::ofstream system_log
;
498 SDL_keysym autorepeating_key
;
499 unsigned autorepeat_phase
= 0;
500 unsigned autorepeat_timecounter
= 0;
501 numeric_setting
autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
502 numeric_setting
autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
505 void poll_inputs_internal() throw(std::bad_alloc
);
509 const size_t audiobuf_size
= 8192;
510 uint16_t audiobuf
[audiobuf_size
];
511 volatile size_t audiobuf_get
= 0;
512 volatile size_t audiobuf_put
= 0;
513 uint64_t sampledup_ctr
= 0;
514 uint64_t sampledup_inc
= 0;
515 uint64_t sampledup_mod
= 1;
516 Uint16 format
= AUDIO_S16SYS
;
518 bool sound_enabled
= true;
520 void calculate_sampledup(uint32_t real_rate
)
523 sampledup_inc
= 64081;
524 sampledup_mod
= 2 * real_rate
+ 64081;
527 void audiocb(void* dummy
, Uint8
* stream
, int len
)
529 static uint16_t lprev
= 32768;
530 static uint16_t rprev
= 32768;
532 lprev
= rprev
= 32768;
533 uint16_t bias
= (format
== AUDIO_S8
|| format
== AUDIO_S16LSB
|| format
== AUDIO_S16MSB
|| format
==
534 AUDIO_S16SYS
) ? 32768 : 0;
537 if(audiobuf_get
== audiobuf_put
) {
541 l
= lprev
= audiobuf
[audiobuf_get
++];
542 r
= rprev
= audiobuf
[audiobuf_get
++];
543 if(audiobuf_get
== audiobuf_size
)
548 if(format
== AUDIO_U8
|| format
== AUDIO_S8
) {
549 stream
[0] = (l
- bias
) >> 8;
551 stream
[1] = (r
- bias
) >> 8;
552 stream
+= (stereo
? 2 : 1);
553 len
-= (stereo
? 2 : 1);
554 } else if(format
== AUDIO_S16SYS
|| format
== AUDIO_U16SYS
) {
555 reinterpret_cast<uint16_t*>(stream
)[0] = (l
- bias
);
557 reinterpret_cast<int16_t*>(stream
)[1] = (r
- bias
);
558 stream
+= (stereo
? 4 : 2);
559 len
-= (stereo
? 4 : 2);
560 } else if(format
== AUDIO_S16LSB
|| format
== AUDIO_U16LSB
) {
561 stream
[0] = (l
- bias
);
562 stream
[1] = (l
- bias
) >> 8;
564 stream
[2] = (r
- bias
);
565 stream
[3] = (r
- bias
) >> 8;
567 stream
+= (stereo
? 4 : 2);
568 len
-= (stereo
? 4 : 2);
569 } else if(format
== AUDIO_S16MSB
|| format
== AUDIO_U16MSB
) {
570 stream
[1] = (l
- bias
);
571 stream
[0] = (l
- bias
) >> 8;
573 stream
[3] = (r
- bias
);
574 stream
[2] = (r
- bias
) >> 8;
576 stream
+= (stereo
? 4 : 2);
577 len
-= (stereo
? 4 : 2);
584 state
= WINSTATE_IDENTIFY
;
585 window::message("Press key to identify.");
586 window::notify_screen_update();
587 poll_inputs_internal();
590 std::string
decode_string(std::string e
)
593 for(size_t i
= 0; i
< e
.length(); i
+= 4) {
595 uint32_t c1
= e
[i
] - 33;
596 uint32_t c2
= e
[i
+ 1] - 33;
597 uint32_t c3
= e
[i
+ 2] - 33;
598 uint32_t c4
= e
[i
+ 3] - 33;
599 uint32_t c
= (c1
<< 18) | (c2
<< 12) | (c3
<< 6) | c4
;
602 } else if(c
< 0x800) {
603 tmp
[0] = 0xC0 | (c
>> 6);
604 tmp
[1] = 0x80 | (c
& 0x3F);
605 } else if(c
< 0x10000) {
606 tmp
[0] = 0xE0 | (c
>> 12);
607 tmp
[1] = 0x80 | ((c
>> 6) & 0x3F);
608 tmp
[2] = 0x80 | (c
& 0x3F);
610 tmp
[0] = 0xF0 | (c
>> 18);
611 tmp
[1] = 0x80 | ((c
>> 12) & 0x3F);
612 tmp
[2] = 0x80 | ((c
>> 6) & 0x3F);
613 tmp
[3] = 0x80 | (c
& 0x3F);
620 void draw_rectangle(uint8_t* data
, uint32_t pitch
, uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
621 uint32_t color
, uint32_t thickness
)
623 for(uint32_t i
= x1
; i
< x2
; i
++)
624 for(uint32_t j
= 0; j
< thickness
; j
++) {
625 reinterpret_cast<uint32_t*>(data
+ pitch
* (y1
+ j
))[i
] = color
;
626 reinterpret_cast<uint32_t*>(data
+ pitch
* (y2
- 1 - j
))[i
] = color
;
628 for(uint32_t i
= y1
; i
< y2
; i
++)
629 for(uint32_t j
= 0; j
< thickness
; j
++) {
630 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x1
+ j
] = color
;
631 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x2
- 1 - j
] = color
;
635 std::vector
<uint32_t> decode_utf8(std::string s
)
637 std::vector
<uint32_t> ret
;
638 for(auto i
= s
.begin(); i
!= s
.end(); i
++) {
639 uint32_t j
= static_cast<uint8_t>(*i
);
645 uint32_t j2
= static_cast<uint8_t>(*(++i
));
646 ret
.push_back((j
- 192) * 64 + (j2
- 128));
648 uint32_t j2
= static_cast<uint8_t>(*(++i
));
649 uint32_t j3
= static_cast<uint8_t>(*(++i
));
650 ret
.push_back((j
- 224) * 4096 + (j2
- 128) * 64 + (j3
- 128));
652 uint32_t j2
= static_cast<uint8_t>(*(++i
));
653 uint32_t j3
= static_cast<uint8_t>(*(++i
));
654 uint32_t j4
= static_cast<uint8_t>(*(++i
));
655 ret
.push_back((j
- 240) * 262144 + (j2
- 128) * 4096 + (j3
- 128) * 64 + (j4
- 128));
661 void draw_string(uint8_t* base
, uint32_t pitch
, std::vector
<uint32_t> s
, uint32_t x
, uint32_t y
,
662 uint32_t maxwidth
, uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
664 base
+= y
* static_cast<size_t>(pitch
) + 4 * x
;
669 uint32_t old_x
= pos_x
;
670 uint32_t curstart
= 16;
671 if(c
== hilite_pos
&& hilite_mode
== 1)
673 if(c
== hilite_pos
&& hilite_mode
== 2)
675 auto g
= find_glyph(si
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
680 for(unsigned j
= 0; j
< 16; j
++) {
681 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
682 for(unsigned i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++)
683 ptr
[old_x
+ i
] = (j
>= curstart
) ? 0xFFFFFFU
: 0;
687 for(unsigned j
= 0; j
< 16; j
++) {
688 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
689 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
690 for(uint32_t i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++) {
691 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
693 b
^= (j
>= curstart
);
694 ptr
[old_x
+ i
] = b
? 0xFFFFFFU
: 0;
700 for(unsigned j
= 0; j
< 16; j
++) {
701 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
702 uint32_t curstart
= 16;
703 if(c
== hilite_pos
&& hilite_mode
== 1)
705 if(c
== hilite_pos
&& hilite_mode
== 2)
707 for(uint32_t i
= pos_x
; i
< maxwidth
; i
++)
708 ptr
[i
] = ((i
- pos_x
) < 8 && j
>= curstart
) ? 0xFFFFFFU
: 0;
712 void draw_string(uint8_t* base
, uint32_t pitch
, std::string s
, uint32_t x
, uint32_t y
, uint32_t maxwidth
,
713 uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
715 draw_string(base
, pitch
, decode_utf8(s
), x
, y
, maxwidth
, hilite_mode
, hilite_pos
);
718 void draw_command(uint8_t* base
, uint32_t pitch
, std::string s
, size_t cursor
, uint32_t x
, uint32_t y
,
719 uint32_t maxwidth
, bool overwrite
)
721 //FIXME, scroll text if too long.
722 uint32_t hilite_mode
= overwrite
? 2 : 1;
723 auto s2
= decode_utf8(s
);
724 draw_string(base
, pitch
, s2
, x
, y
, maxwidth
, hilite_mode
, cursor
);
727 void draw_modal_dialog(SDL_Surface
* surf
, std::string msg
, bool confirm
)
734 msg
= msg
+ "\n\nHit Enter to confirm, Esc to cancel";
736 msg
= msg
+ "\n\nHit Enter or Esc to dismiss";
737 auto s2
= decode_utf8(msg
);
739 auto g
= find_glyph(i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
740 if(pos_x
+ g
.first
> width
)
741 width
= static_cast<uint32_t>(pos_x
+ g
.first
);
742 if(pos_y
+ 16 > static_cast<int32_t>(height
))
743 height
= static_cast<uint32_t>(pos_y
+ 16);
749 if(width
+ 12 >= static_cast<uint32_t>(surf
->w
)) {
754 x1
= (surf
->w
- width
) / 2;
757 if(height
+ 12 >= static_cast<uint32_t>(surf
->h
)) {
762 y1
= (surf
->h
- height
) / 2;
765 for(uint32_t j
= y1
- 6; j
< y2
+ 6; j
++)
766 memset(reinterpret_cast<uint8_t*>(surf
->pixels
) + j
* surf
->pitch
+ 4 * (x1
- 6), 0,
768 uint32_t bordercolor
= 0xFF8000;
769 draw_rectangle(reinterpret_cast<uint8_t*>(surf
->pixels
), surf
->pitch
, x1
- 4, y1
- 4, x2
+ 4, y2
+ 4,
777 auto g
= find_glyph(i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
778 if(static_cast<uint32_t>(pos_y
) > height
)
780 uint8_t* base
= reinterpret_cast<uint8_t*>(surf
->pixels
) + (y1
+ oy
) * surf
->pitch
+
784 for(unsigned j
= 0; j
< 16; j
++) {
785 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ surf
->pitch
* j
);
786 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
787 for(uint32_t i
= 0; i
< g
.first
&& (ox
+ i
) < width
; i
++) {
788 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
790 ptr
[i
] = b
? bordercolor
: 0;
797 void do_keyboard_command_edit(SDL_keysym k
)
799 //These are not command edit!
800 if(k
.sym
== SDLK_ESCAPE
)
802 if(k
.sym
== SDLK_RETURN
)
804 if(k
.sym
== SDLK_KP_ENTER
)
806 //Map keys a bit if numlock is off.
807 if((k
.mod
& KMOD_NUM
) == 0) {
809 case SDLK_KP0
: k
.sym
= SDLK_INSERT
; break;
810 case SDLK_KP1
: k
.sym
= SDLK_END
; break;
811 case SDLK_KP2
: k
.sym
= SDLK_DOWN
; break;
812 case SDLK_KP3
: k
.sym
= SDLK_PAGEDOWN
; break;
813 case SDLK_KP4
: k
.sym
= SDLK_LEFT
; break;
814 case SDLK_KP5
: return;
815 case SDLK_KP6
: k
.sym
= SDLK_RIGHT
; break;
816 case SDLK_KP7
: k
.sym
= SDLK_HOME
; break;
817 case SDLK_KP8
: k
.sym
= SDLK_UP
; break;
818 case SDLK_KP9
: k
.sym
= SDLK_PAGEUP
; break;
819 case SDLK_KP_PERIOD
: k
.sym
= SDLK_DELETE
; break;
824 //Special editing operations.
827 command_overwrite
= !command_overwrite
;
828 window::notify_screen_update();
831 command_cursor
= command_buf
.length();
832 window::notify_screen_update();
836 if(commandhistory_itr
!= commandhistory
.begin()) {
837 commandhistory_itr
--;
838 command_buf
= *commandhistory_itr
;
839 if(command_cursor
> command_buf
.length())
840 command_cursor
= command_buf
.length();
842 window::notify_screen_update();
845 command_cursor
= (command_cursor
> 0) ? (command_cursor
- 4) : 0;
846 window::notify_screen_update();
849 command_cursor
= (command_cursor
< command_buf
.length()) ? (command_cursor
+ 4) :
850 command_buf
.length();
851 window::notify_screen_update();
855 window::notify_screen_update();
859 auto tmp
= commandhistory_itr
;
860 if(++tmp
!= commandhistory
.end()) {
861 commandhistory_itr
++;
862 command_buf
= *commandhistory_itr
;
863 if(command_cursor
> command_buf
.length())
864 command_cursor
= command_buf
.length();
866 window::notify_screen_update();
870 if(command_cursor
< command_buf
.length())
871 command_buf
= command_buf
.substr(0, command_cursor
) +
872 command_buf
.substr(command_cursor
+ 4);
873 window::notify_screen_update();
874 *commandhistory_itr
= command_buf
;
877 if(command_cursor
> 0) {
878 command_buf
= command_buf
.substr(0, command_cursor
- 4) +
879 command_buf
.substr(command_cursor
);
882 window::notify_screen_update();
883 *commandhistory_itr
= command_buf
;
889 //Not a special editing operation, insert/overwrite a character.
890 uint32_t code
= k
.unicode
;
893 uint8_t c1
= 33 + ((code
>> 18) & 0x3F);
894 uint8_t c2
= 33 + ((code
>> 12) & 0x3F);
895 uint8_t c3
= 33 + ((code
>> 6) & 0x3F);
896 uint8_t c4
= 33 + (code
& 0x3F);
897 if(command_overwrite
&& command_cursor
< command_buf
.length()) {
898 command_buf
[command_cursor
] = c1
;
899 command_buf
[command_cursor
+ 1] = c2
;
900 command_buf
[command_cursor
+ 2] = c3
;
901 command_buf
[command_cursor
+ 3] = c4
;
904 std::string foo
= " ";
909 command_buf
= command_buf
.substr(0, command_cursor
) + foo
+ command_buf
.substr(command_cursor
);
912 *commandhistory_itr
= command_buf
;
913 window::notify_screen_update();
916 void do_event(SDL_Event
& e
) throw(std::bad_alloc
)
919 alarm(WATCHDOG_TIMEOUT
);
921 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
&& e
.key
.keysym
.mod
== (KMOD_LCTRL
|
924 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
926 window::notify_screen_update();
929 if(e
.type
== SDL_ACTIVEEVENT
&& e
.active
.gain
&& e
.active
.state
== SDL_APPACTIVE
) {
930 window::notify_screen_update();
933 if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_KEYUP
)
934 key
= e
.key
.keysym
.sym
;
936 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_IDENTIFY
)
938 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_MODAL
) {
939 delayed_close_flag
= true;
942 if(e
.type
== SDL_QUIT
) {
943 window_callback::do_close();
944 state
= WINSTATE_NORMAL
;
949 case WINSTATE_NORMAL
:
950 if(e
.type
== SDL_MOUSEBUTTONDOWN
|| e
.type
== SDL_MOUSEBUTTONUP
) {
951 int32_t xc
= e
.button
.x
;
952 int32_t yc
= e
.button
.y
;
953 xc
= (xc
- 6 - vc_xoffset
) / vc_hscl
;
954 yc
= (yc
- 6 - vc_yoffset
) / vc_vscl
;
955 if(e
.button
.button
== SDL_BUTTON_LEFT
) {
956 if(e
.button
.state
== SDL_PRESSED
)
961 if(e
.button
.button
== SDL_BUTTON_MIDDLE
) {
962 if(e
.button
.state
== SDL_PRESSED
)
967 if(e
.button
.button
== SDL_BUTTON_RIGHT
) {
968 if(e
.button
.state
== SDL_PRESSED
)
973 window_callback::do_click(xc
, yc
, mouse_mask
);
975 if(e
.type
== SDL_KEYDOWN
&& key
== SDLK_ESCAPE
)
977 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
978 state
= WINSTATE_COMMAND
;
981 commandhistory
.push_front("");
982 if(commandhistory
.size() > MAXHISTORY
)
983 commandhistory
.pop_back();
984 commandhistory_itr
= commandhistory
.begin();
985 window::notify_screen_update();
986 poll_inputs_internal();
989 process_input_event(&e
, false);
992 //Send the key and eat it (prevent input from getting confused).
993 keygroup::set_exclusive_key_listener(&keyeater
);
994 process_input_event(&e
, false),
995 keygroup::set_exclusive_key_listener(NULL
);
996 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
997 state
= WINSTATE_NORMAL
;
999 modal_return_flag
= true;
1001 window::notify_screen_update(true);
1004 if(e
.type
== SDL_KEYUP
&& (key
== SDLK_RETURN
|| key
== SDLK_KP_ENTER
)) {
1005 state
= WINSTATE_NORMAL
;
1006 modal_return_flag
= true;
1008 window::notify_screen_update(true);
1012 case WINSTATE_COMMAND
:
1013 //Send the key and eat it (prevent input from getting confused).
1014 keygroup::set_exclusive_key_listener(&keyeater
);
1015 process_input_event(&e
, false),
1016 keygroup::set_exclusive_key_listener(NULL
);
1017 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
) {
1018 state
= WINSTATE_NORMAL
;
1020 window::notify_screen_update();
1021 if(commandhistory
.front() == "")
1022 commandhistory
.pop_front();
1025 if(e
.type
== SDL_KEYUP
&& (e
.key
.keysym
.sym
== SDLK_RETURN
||
1026 e
.key
.keysym
.sym
== SDLK_KP_ENTER
)) {
1027 state
= WINSTATE_NORMAL
;
1028 if(commandhistory
.front() == "")
1029 commandhistory
.pop_front();
1030 command::invokeC(decode_string(command_buf
));
1032 window::notify_screen_update();
1033 autorepeat_phase
= 0;
1036 if(e
.type
== SDL_KEYDOWN
) {
1037 autorepeating_key
= e
.key
.keysym
;
1038 autorepeat_phase
= 1;
1039 autorepeat_timecounter
= 0;
1040 do_keyboard_command_edit(e
.key
.keysym
);
1041 } else if(e
.type
== SDL_KEYUP
) {
1042 autorepeat_phase
= 0;
1044 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
1045 autorepeat_timecounter
++;
1046 if(!autorepeat_phase
)
1048 unsigned timeout
= (autorepeat_phase
== 1) ? autorepeat_first
: autorepeat_subsequent
;
1049 if(autorepeat_timecounter
>= timeout
) {
1050 do_keyboard_command_edit(autorepeating_key
);
1051 autorepeat_timecounter
= 0;
1052 autorepeat_phase
= 2;
1056 case WINSTATE_IDENTIFY
:
1057 process_input_event(&e
, true);
1065 SDL_initialized
= true;
1067 signal(SIGALRM
, sigalrm_handler
);
1068 alarm(WATCHDOG_TIMEOUT
);
1071 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
1072 time_t curtime
= __real_time(NULL
);
1073 struct tm
* tm
= localtime(&curtime
);
1075 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1076 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1077 system_log
<< "lsnes started at " << buffer
<< std::endl
;
1078 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1080 SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
| SDL_INIT_JOYSTICK
| SDL_INIT_TIMER
);
1081 SDL_EnableUNICODE(true);
1083 tid
= SDL_AddTimer(MIN_UPDATE_TIME
/ 1000 + 1, timer_cb
, NULL
);
1085 state
= WINSTATE_NORMAL
;
1086 current_screen
= NULL
;
1087 pause_active
= false;
1090 command_overwrite
= false;
1093 modal_return_flag
= false;
1094 delayed_close_flag
= false;
1095 messagebuffer_next_seq
= 0;
1096 messagebuffer_first_seq
= 0;
1097 messagebuffer_first_show
= 0;
1098 console_mode
= false;
1099 maxmessages
= MAXMESSAGES
;
1101 notify_screen_update();
1102 std::string windowname
= "lsnes-" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
1103 SDL_WM_SetCaption(windowname
.c_str(), "lsnes");
1107 SDL_AudioSpec
* desired
= new SDL_AudioSpec();
1108 SDL_AudioSpec
* obtained
= new SDL_AudioSpec();
1110 desired
->freq
= 44100;
1111 desired
->format
= AUDIO_S16SYS
;
1112 desired
->channels
= 2;
1113 desired
->samples
= 8192;
1114 desired
->callback
= audiocb
;
1115 desired
->userdata
= NULL
;
1117 if(SDL_OpenAudio(desired
, obtained
) < 0) {
1118 message("Audio can't be initialized, audio playback disabled");
1126 //Fill the parameters.
1127 calculate_sampledup(obtained
->freq
);
1128 format
= obtained
->format
;
1129 stereo
= (obtained
->channels
== 2);
1136 time_t curtime
= time(NULL
);
1137 struct tm
* tm
= localtime(&curtime
);
1139 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1140 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1141 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
1142 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1145 SDL_RemoveTimer(tid
);
1149 SDL_initialized
= false;
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 window_callback::do_close();
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 if(SDL_initialized
) {
1176 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
);
1177 system_log
<< forlog
<< std::endl
;
1179 std::cerr
<< msg2
<< std::endl
;
1182 if(SDL_initialized
) {
1183 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
.substr(0, s
));
1184 system_log
<< forlog
<< std::endl
;
1186 std::cerr
<< msg2
.substr(0, s
) << std::endl
;
1187 msg2
= msg2
.substr(s
+ 1);
1191 if(locked_mode
&& messagebuffer_first_show
+ maxmessages
< messagebuffer_next_seq
)
1192 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1194 while(messagebuffer
.size() > MSGHISTORY
) {
1195 messagebuffer
.erase(messagebuffer_first_seq
++);
1196 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1197 messagebuffer_first_show
= messagebuffer_first_seq
;
1199 notify_screen_update();
1202 void window::set_main_surface(screen
& scr
) throw()
1204 current_screen
= &scr
;
1205 notify_screen_update(true);
1210 bool is_time_for_screen_update(bool full
)
1212 uint64_t curtime
= get_utime();
1213 //Always do full updates.
1214 if(!full
&& last_ui_update
< curtime
&& last_ui_update
+ MIN_UPDATE_TIME
> curtime
) {
1215 screen_is_dirty
= true;
1218 last_ui_update
= curtime
;
1219 screen_is_dirty
= false;
1223 std::pair
<uint32_t, uint32_t> compute_screen_size(uint32_t width
, uint32_t height
)
1229 return std::make_pair(width
, height
);
1232 std::pair
<uint32_t, uint32_t> compute_window_size(uint32_t width
, uint32_t height
)
1234 auto g
= compute_screen_size(width
, height
);
1235 uint32_t win_w
= ((g
.first
+ 15) >> 4 << 4) + 278;
1236 uint32_t win_h
= g
.second
+ MAXMESSAGES
* 16 + 48;
1237 return std::make_pair(win_w
, win_h
);
1243 std::ostringstream y
;
1244 y
<< get_framerate();
1245 emustatus
["FPS"] = y
.str();
1250 void redraw_borders(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
,
1251 std::pair
<uint32_t, uint32_t> windowsize
)
1253 //Blank the screen and draw borders.
1254 memset(swsurf
->pixels
, 0, windowsize
.second
* swsurf
->pitch
);
1255 uint32_t bordercolor
= 0x00FF00;
1256 uint32_t msgbox_min_x
= 2;
1257 uint32_t msgbox_min_y
= 2;
1258 uint32_t msgbox_max_x
= windowsize
.first
- 2;
1259 uint32_t msgbox_max_y
= windowsize
.second
- 28;
1260 uint32_t cmdbox_min_x
= 2;
1261 uint32_t cmdbox_max_x
= windowsize
.first
- 2;
1262 uint32_t cmdbox_min_y
= windowsize
.second
- 26;
1263 uint32_t cmdbox_max_y
= windowsize
.second
- 2;
1265 uint32_t scrbox_min_x
= 2;
1266 uint32_t scrbox_max_x
= screensize
.first
+ 10;
1267 uint32_t scrbox_min_y
= 2;
1268 uint32_t scrbox_max_y
= screensize
.second
+ 10;
1269 uint32_t stsbox_min_x
= screensize
.first
+ 12;
1270 uint32_t stsbox_max_x
= windowsize
.first
- 2;
1271 uint32_t stsbox_min_y
= 2;
1272 uint32_t stsbox_max_y
= screensize
.second
+ 10;
1273 msgbox_min_y
= screensize
.second
+ 12;
1274 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, scrbox_min_x
,
1275 scrbox_min_y
, scrbox_max_x
, scrbox_max_y
, bordercolor
, 2);
1276 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, stsbox_min_x
,
1277 stsbox_min_y
, stsbox_max_x
, stsbox_max_y
, bordercolor
, 2);
1279 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, msgbox_min_x
,
1280 msgbox_min_y
, msgbox_max_x
, msgbox_max_y
, bordercolor
, 2);
1281 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, cmdbox_min_x
,
1282 cmdbox_min_y
, cmdbox_max_x
, cmdbox_max_y
, bordercolor
, 2);
1285 void draw_main_screen(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
)
1287 uint32_t cw
= current_screen
? current_screen
->width
: 0;
1288 uint32_t ch
= current_screen
? current_screen
->height
: 0;
1289 //Blank parts not drawn.
1290 for(uint32_t i
= 6; i
< ch
+ 6; i
++)
1291 memset(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + i
* swsurf
->pitch
+ 24 + 4 * cw
, 0,
1292 4 * (screensize
.first
- cw
));
1293 for(uint32_t i
= ch
+ 6; i
< screensize
.second
+ 6; i
++)
1294 memset(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + i
* swsurf
->pitch
+ 24, 0,
1295 4 * screensize
.first
);
1296 if(current_screen
) {
1297 for(uint32_t i
= 0; i
< ch
; i
++)
1298 memcpy(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + (i
+ 6) * swsurf
->pitch
+ 24,
1299 reinterpret_cast<uint8_t*>(current_screen
->memory
) + current_screen
->pitch
* i
,
1304 void draw_status_area(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
)
1306 uint32_t status_x
= screensize
.first
+ 16;
1307 uint32_t status_y
= 6;
1308 for(auto i
: emustatus
) {
1309 std::string msg
= i
.first
+ " " + i
.second
;
1310 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, msg
, status_x
, status_y
,
1314 while(status_y
- 6 < screensize
.second
/ 16 * 16) {
1315 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "", status_x
, status_y
,
1321 void draw_messages(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
,
1322 std::pair
<uint32_t, uint32_t> windowsize
)
1326 message_y
= screensize
.second
+ 16;
1329 for(size_t j
= 0; j
< maxmessages
; j
++)
1331 std::ostringstream o
;
1332 if(messagebuffer_first_show
+ j
< messagebuffer_next_seq
)
1333 o
<< (messagebuffer_first_show
+ j
+ 1) << ": "
1334 << messagebuffer
[messagebuffer_first_show
+ j
];
1335 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, o
.str(), 6,
1336 message_y
+ 16 * j
, windowsize
.first
- 12);
1339 if(messagebuffer_next_seq
- messagebuffer_first_show
> maxmessages
)
1341 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "--More--",
1342 windowsize
.first
- 76, message_y
+ 16 * maxmessages
- 16, 64);
1347 void draw_command(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> windowsize
)
1349 uint32_t command_y
= windowsize
.second
- 22;
1351 std::string command_showas
= decode_string(command_buf
);
1352 if(state
== WINSTATE_COMMAND
)
1353 draw_command(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, command_showas
,
1354 command_cursor
/ 4, 6, command_y
, windowsize
.first
- 12, command_overwrite
);
1356 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "", 6,
1357 command_y
, windowsize
.first
- 12);
1363 void window::notify_screen_update(bool full
) throw()
1365 bool resize_screen
= false;
1366 if(!is_time_for_screen_update(full
))
1368 auto windowsize
= compute_window_size(current_screen
? current_screen
->width
: 0, current_screen
?
1369 current_screen
->height
: 0);
1370 auto screensize
= compute_screen_size(current_screen
? current_screen
->width
: 0, current_screen
?
1371 current_screen
->height
: 0);
1375 if(!hwsurf
|| windowsize
!= current_windowsize
) {
1376 //Create/Resize the window.
1377 SDL_Surface
* hwsurf2
= SDL_SetVideoMode(windowsize
.first
, windowsize
.second
, 0, SDL_SWSURFACE
|
1379 SDL_Surface
* swsurf2
= SDL_CreateRGBSurface(SDL_SWSURFACE
, windowsize
.first
, windowsize
.second
, 32,
1380 0xFF0000, 0x00FF00, 0x0000FF, 0);
1381 if(!hwsurf2
|| !swsurf2
) {
1382 //We are in too fucked up state to even print error as message.
1383 std::cout
<< "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl
;
1387 SDL_FreeSurface(swsurf
);
1391 current_windowsize
= windowsize
;
1394 SDL_LockSurface(swsurf
);
1396 redraw_borders(swsurf
, screensize
, windowsize
);
1398 draw_main_screen(swsurf
, screensize
);
1399 draw_status_area(swsurf
, screensize
);
1401 draw_messages(swsurf
, screensize
, windowsize
);
1402 draw_command(swsurf
, windowsize
);
1404 //Draw modal dialog.
1405 if(state
== WINSTATE_MODAL
)
1407 draw_modal_dialog(swsurf
, modmsg
, modconfirm
);
1410 SDL_UnlockSurface(swsurf
);
1411 SDL_BlitSurface(swsurf
, NULL
, hwsurf
, NULL
);
1412 SDL_UpdateRect(hwsurf
, 0, 0, 0, 0);
1415 void poll_inputs_internal() throw(std::bad_alloc
)
1418 while(state
!= WINSTATE_NORMAL
) {
1419 if(SDL_WaitEvent(&e
))
1421 if(delayed_close_flag
) {
1422 state
= WINSTATE_NORMAL
;
1428 void window::poll_inputs() throw(std::bad_alloc
)
1432 assert(state
== WINSTATE_NORMAL
);
1433 if(!pause_active
&& !SDL_PollEvent(&e
))
1435 else if(!pause_active
)
1437 else if(SDL_WaitEvent(&e
))
1442 std::map
<std::string
, std::string
>& window::get_emustatus() throw()
1447 void window::paused(bool enable
) throw()
1449 pause_active
= enable
;
1450 notify_screen_update();
1453 void window::sound_enable(bool enable
) throw()
1455 sound_enabled
= enable
;
1456 SDL_PauseAudio(enable
? 0 : 1);
1461 function_ptr_command
<const std::string
&> enable_sound("enable-sound", "Enable/Disable sound",
1462 "Syntax: enable-sound <on/off>\nEnable or disable sound.\n",
1463 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
1464 std::string s
= args
;
1465 if(s
== "on" || s
== "true" || s
== "1" || s
== "enable" || s
== "enabled")
1466 window::sound_enable(true);
1467 else if(s
== "off" || s
== "false" || s
== "0" || s
== "disable" || s
== "disabled")
1468 window::sound_enable(false);
1470 throw std::runtime_error("Bad sound setting");
1473 function_ptr_command
<> identify_key("identify-key", "Identify a key",
1474 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1475 []() throw(std::bad_alloc
, std::runtime_error
) {
1479 function_ptr_command
<> scroll_up("scroll-up", "Scroll messages a page up",
1480 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1481 []() throw(std::bad_alloc
, std::runtime_error
) {
1482 if(messagebuffer_first_show
> maxmessages
)
1483 messagebuffer_first_show
-= maxmessages
;
1485 messagebuffer_first_show
= 0;
1486 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1487 messagebuffer_first_show
= messagebuffer_first_seq
;
1488 window::notify_screen_update();
1491 function_ptr_command
<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1492 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1493 []() throw(std::bad_alloc
, std::runtime_error
) {
1494 messagebuffer_first_show
= messagebuffer_first_seq
;
1495 window::notify_screen_update();
1498 function_ptr_command
<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1499 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1500 []() throw(std::bad_alloc
, std::runtime_error
) {
1501 if(messagebuffer_next_seq
< maxmessages
)
1502 messagebuffer_first_show
= 0;
1504 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1505 window::notify_screen_update();
1508 function_ptr_command
<> scrolldown("scroll-down", "Scroll messages a page down",
1509 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1510 []() throw(std::bad_alloc
, std::runtime_error
) {
1511 messagebuffer_first_show
+= maxmessages
;
1512 if(messagebuffer_next_seq
< maxmessages
)
1513 messagebuffer_first_show
= 0;
1514 else if(messagebuffer_next_seq
< messagebuffer_first_show
+ maxmessages
)
1515 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1516 window::notify_screen_update();
1519 function_ptr_command
<> toggle_console("toggle-console", "Toggle console between small and full window",
1520 "Syntax: toggle-console\nToggles console between small and large.\n",
1521 []() throw(std::bad_alloc
, std::runtime_error
) {
1522 console_mode
= !console_mode
;
1524 maxmessages
= swsurf
? (swsurf
->h
- 38) / 16 : 36;
1526 maxmessages
= MAXMESSAGES
;
1527 if(messagebuffer_next_seq
< maxmessages
)
1528 messagebuffer_first_show
= 0;
1530 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1531 window::notify_screen_update(true);
1535 void window::wait_usec(uint64_t usec
) throw(std::bad_alloc
)
1537 wait_canceled
= false;
1538 uint64_t end_at
= get_utime() + usec
;
1539 while(!wait_canceled
) {
1541 while(SDL_PollEvent(&e
))
1543 uint64_t curtime
= get_utime();
1544 if(curtime
> end_at
|| wait_canceled
)
1546 if(end_at
> curtime
+ 10000)
1549 ::wait_usec(end_at
- curtime
);
1551 wait_canceled
= false;
1554 void window::fatal_error() throw()
1557 message("PANIC: Cannot continue, press ESC or close window to exit.");
1559 notify_screen_update(true);
1564 time_t curtime
= time(NULL
);
1565 struct tm
* tm
= localtime(&curtime
);
1567 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1568 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1569 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
1570 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1574 if(SDL_WaitEvent(&e
)) {
1575 if(e
.type
== SDL_QUIT
)
1577 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
)
1583 void window::cancel_wait() throw()
1585 wait_canceled
= true;
1588 void window::play_audio_sample(uint16_t left
, uint16_t right
) throw()
1590 sampledup_ctr
+= sampledup_inc
;
1591 while(sampledup_ctr
< sampledup_mod
) {
1592 audiobuf
[audiobuf_put
++] = left
;
1593 audiobuf
[audiobuf_put
++] = right
;
1594 if(audiobuf_put
== audiobuf_size
)
1596 sampledup_ctr
+= sampledup_inc
;
1598 sampledup_ctr
-= sampledup_mod
;
1601 void window::set_window_compensation(uint32_t xoffset
, uint32_t yoffset
, uint32_t hscl
, uint32_t vscl
)
1603 vc_xoffset
= xoffset
;
1604 vc_yoffset
= yoffset
;