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
30 extern uint64_t in_paint_time
;
37 void sigalrm_handler(int s
)
42 Uint32
timer_cb(Uint32 interval
, void* param
)
45 e
.type
= SDL_USEREVENT
;
56 } modifiers_table
[] = {
58 { "lctrl", "ctrl", KMOD_LCTRL
},
59 { "rctrl", "ctrl", KMOD_RCTRL
},
61 { "lalt", "alt", KMOD_LALT
},
62 { "ralt", "alt", KMOD_RALT
},
64 { "lshift", "shift", KMOD_LSHIFT
},
65 { "rshift", "shift", KMOD_RSHIFT
},
67 { "lmeta", "meta", KMOD_LMETA
},
68 { "rmeta", "meta", KMOD_RMETA
},
69 { "num", NULL
, KMOD_NUM
},
70 { "caps", NULL
, KMOD_CAPS
},
71 { "mode", NULL
, KMOD_MODE
},
80 {"backspace", SDLK_BACKSPACE
},
82 {"clear", SDLK_CLEAR
},
83 {"return", SDLK_RETURN
},
84 {"pause", SDLK_PAUSE
},
85 {"escape", SDLK_ESCAPE
},
86 {"space", SDLK_SPACE
},
87 {"exclaim", SDLK_EXCLAIM
},
88 {"quotedbl", SDLK_QUOTEDBL
},
90 {"dollar", SDLK_DOLLAR
},
91 {"ampersand", SDLK_AMPERSAND
},
92 {"quote", SDLK_QUOTE
},
93 {"leftparen", SDLK_LEFTPAREN
},
94 {"rightparen", SDLK_RIGHTPAREN
},
95 {"asterisk", SDLK_ASTERISK
},
97 {"comma", SDLK_COMMA
},
98 {"minus", SDLK_MINUS
},
99 {"period", SDLK_PERIOD
},
100 {"slash", SDLK_SLASH
},
111 {"colon", SDLK_COLON
},
112 {"semicolon", SDLK_SEMICOLON
},
113 {"less", SDLK_LESS
},
114 {"equals", SDLK_EQUALS
},
115 {"greater", SDLK_GREATER
},
116 {"question", SDLK_QUESTION
},
118 {"leftbracket", SDLK_LEFTBRACKET
},
119 {"backslash", SDLK_BACKSLASH
},
120 {"rightbracket", SDLK_RIGHTBRACKET
},
121 {"caret", SDLK_CARET
},
122 {"underscore", SDLK_UNDERSCORE
},
123 {"backquote", SDLK_BACKQUOTE
},
150 {"delete", SDLK_DELETE
},
151 {"world_0", SDLK_WORLD_0
},
152 {"world_1", SDLK_WORLD_1
},
153 {"world_2", SDLK_WORLD_2
},
154 {"world_3", SDLK_WORLD_3
},
155 {"world_4", SDLK_WORLD_4
},
156 {"world_5", SDLK_WORLD_5
},
157 {"world_6", SDLK_WORLD_6
},
158 {"world_7", SDLK_WORLD_7
},
159 {"world_8", SDLK_WORLD_8
},
160 {"world_9", SDLK_WORLD_9
},
161 {"world_10", SDLK_WORLD_10
},
162 {"world_11", SDLK_WORLD_11
},
163 {"world_12", SDLK_WORLD_12
},
164 {"world_13", SDLK_WORLD_13
},
165 {"world_14", SDLK_WORLD_14
},
166 {"world_15", SDLK_WORLD_15
},
167 {"world_16", SDLK_WORLD_16
},
168 {"world_17", SDLK_WORLD_17
},
169 {"world_18", SDLK_WORLD_18
},
170 {"world_19", SDLK_WORLD_19
},
171 {"world_20", SDLK_WORLD_20
},
172 {"world_21", SDLK_WORLD_21
},
173 {"world_22", SDLK_WORLD_22
},
174 {"world_23", SDLK_WORLD_23
},
175 {"world_24", SDLK_WORLD_24
},
176 {"world_25", SDLK_WORLD_25
},
177 {"world_26", SDLK_WORLD_26
},
178 {"world_27", SDLK_WORLD_27
},
179 {"world_28", SDLK_WORLD_28
},
180 {"world_29", SDLK_WORLD_29
},
181 {"world_30", SDLK_WORLD_30
},
182 {"world_31", SDLK_WORLD_31
},
183 {"world_32", SDLK_WORLD_32
},
184 {"world_33", SDLK_WORLD_33
},
185 {"world_34", SDLK_WORLD_34
},
186 {"world_35", SDLK_WORLD_35
},
187 {"world_36", SDLK_WORLD_36
},
188 {"world_37", SDLK_WORLD_37
},
189 {"world_38", SDLK_WORLD_38
},
190 {"world_39", SDLK_WORLD_39
},
191 {"world_40", SDLK_WORLD_40
},
192 {"world_41", SDLK_WORLD_41
},
193 {"world_42", SDLK_WORLD_42
},
194 {"world_43", SDLK_WORLD_43
},
195 {"world_44", SDLK_WORLD_44
},
196 {"world_45", SDLK_WORLD_45
},
197 {"world_46", SDLK_WORLD_46
},
198 {"world_47", SDLK_WORLD_47
},
199 {"world_48", SDLK_WORLD_48
},
200 {"world_49", SDLK_WORLD_49
},
201 {"world_50", SDLK_WORLD_50
},
202 {"world_51", SDLK_WORLD_51
},
203 {"world_52", SDLK_WORLD_52
},
204 {"world_53", SDLK_WORLD_53
},
205 {"world_54", SDLK_WORLD_54
},
206 {"world_55", SDLK_WORLD_55
},
207 {"world_56", SDLK_WORLD_56
},
208 {"world_57", SDLK_WORLD_57
},
209 {"world_58", SDLK_WORLD_58
},
210 {"world_59", SDLK_WORLD_59
},
211 {"world_60", SDLK_WORLD_60
},
212 {"world_61", SDLK_WORLD_61
},
213 {"world_62", SDLK_WORLD_62
},
214 {"world_63", SDLK_WORLD_63
},
215 {"world_64", SDLK_WORLD_64
},
216 {"world_65", SDLK_WORLD_65
},
217 {"world_66", SDLK_WORLD_66
},
218 {"world_67", SDLK_WORLD_67
},
219 {"world_68", SDLK_WORLD_68
},
220 {"world_69", SDLK_WORLD_69
},
221 {"world_70", SDLK_WORLD_70
},
222 {"world_71", SDLK_WORLD_71
},
223 {"world_72", SDLK_WORLD_72
},
224 {"world_73", SDLK_WORLD_73
},
225 {"world_74", SDLK_WORLD_74
},
226 {"world_75", SDLK_WORLD_75
},
227 {"world_76", SDLK_WORLD_76
},
228 {"world_77", SDLK_WORLD_77
},
229 {"world_78", SDLK_WORLD_78
},
230 {"world_79", SDLK_WORLD_79
},
231 {"world_80", SDLK_WORLD_80
},
232 {"world_81", SDLK_WORLD_81
},
233 {"world_82", SDLK_WORLD_82
},
234 {"world_83", SDLK_WORLD_83
},
235 {"world_84", SDLK_WORLD_84
},
236 {"world_85", SDLK_WORLD_85
},
237 {"world_86", SDLK_WORLD_86
},
238 {"world_87", SDLK_WORLD_87
},
239 {"world_88", SDLK_WORLD_88
},
240 {"world_89", SDLK_WORLD_89
},
241 {"world_90", SDLK_WORLD_90
},
242 {"world_91", SDLK_WORLD_91
},
243 {"world_92", SDLK_WORLD_92
},
244 {"world_93", SDLK_WORLD_93
},
245 {"world_94", SDLK_WORLD_94
},
246 {"world_95", SDLK_WORLD_95
},
257 {"kp_period", SDLK_KP_PERIOD
},
258 {"kp_divide", SDLK_KP_DIVIDE
},
259 {"kp_multiply", SDLK_KP_MULTIPLY
},
260 {"kp_minus", SDLK_KP_MINUS
},
261 {"kp_plus", SDLK_KP_PLUS
},
262 {"kp_enter", SDLK_KP_ENTER
},
263 {"kp_equals", SDLK_KP_EQUALS
},
265 {"down", SDLK_DOWN
},
266 {"right", SDLK_RIGHT
},
267 {"left", SDLK_LEFT
},
268 {"insert", SDLK_INSERT
},
269 {"home", SDLK_HOME
},
271 {"pageup", SDLK_PAGEUP
},
272 {"pagedown", SDLK_PAGEDOWN
},
288 {"numlock", SDLK_NUMLOCK
},
289 {"capslock", SDLK_CAPSLOCK
},
290 {"scrollock", SDLK_SCROLLOCK
},
291 {"rshift", SDLK_RSHIFT
},
292 {"lshift", SDLK_LSHIFT
},
293 {"rctrl", SDLK_RCTRL
},
294 {"lctrl", SDLK_LCTRL
},
295 {"ralt", SDLK_RALT
},
296 {"lalt", SDLK_LALT
},
297 {"rmeta", SDLK_RMETA
},
298 {"lmeta", SDLK_LMETA
},
299 {"lsuper", SDLK_LSUPER
},
300 {"rsuper", SDLK_RSUPER
},
301 {"mode", SDLK_MODE
},
302 {"compose", SDLK_COMPOSE
},
303 {"help", SDLK_HELP
},
304 {"print", SDLK_PRINT
},
305 {"sysreq", SDLK_SYSREQ
},
306 {"break", SDLK_BREAK
},
307 {"menu", SDLK_MENU
},
308 {"power", SDLK_POWER
},
309 {"euro", SDLK_EURO
},
310 {"undo", SDLK_UNDO
},
314 std::map
<unsigned, modifier
*> supported_modifiers
;
315 std::map
<unsigned, keygroup
*> scancodekeys
;
316 std::map
<unsigned, keygroup
*> symbolkeys
;
317 #ifndef SDL_NO_JOYSTICK
318 std::map
<unsigned, keygroup
*> joyaxis
;
319 std::map
<unsigned, keygroup
*> joybutton
;
320 std::map
<unsigned, keygroup
*> joyhat
;
325 struct sdl_modifier
* m
= modifiers_table
;
329 m2
= new modifier(m
->name
, m
->linkname
);
331 m2
= new modifier(m
->name
);
333 supported_modifiers
[m
->sdlvalue
] = m2
;
336 struct sdl_key
* k
= keys_table
;
338 symbolkeys
[k
->symbol
] = new keygroup(k
->name
, keygroup::KT_KEY
);
341 for(unsigned i
= 0; i
< 256; i
++) {
342 std::ostringstream x
;
344 scancodekeys
[i
] = new keygroup(x
.str(), keygroup::KT_KEY
);
348 void init_joysticks()
350 #ifndef SDL_NO_JOYSTICK
351 int joysticks
= SDL_NumJoysticks();
353 window::out() << "No joysticks detected." << std::endl
;
355 window::out() << joysticks
<< " joystick(s) detected." << std::endl
;
356 for(int i
= 0; i
< joysticks
; i
++) {
357 SDL_Joystick
* j
= SDL_JoystickOpen(i
);
359 window::out() << "Joystick #" << i
<< ": Can't open!" << std::endl
;
362 window::out() << "Joystick #" << i
<< ": " << SDL_JoystickName(i
) << "("
363 << SDL_JoystickNumAxes(j
) << " axes, " << SDL_JoystickNumButtons(j
)
364 << " buttons, " << SDL_JoystickNumHats(j
) << " hats)." << std::endl
;
365 for(int k
= 0; k
< SDL_JoystickNumAxes(j
); k
++) {
366 unsigned num
= 256 * i
+ k
;
367 std::ostringstream x
;
368 x
<< "joystick" << i
<< "axis" << k
;
369 joyaxis
[num
] = new keygroup(x
.str(), keygroup::KT_AXIS_PAIR
);
371 for(int k
= 0; k
< SDL_JoystickNumButtons(j
); k
++) {
372 unsigned num
= 256 * i
+ k
;
373 std::ostringstream x
;
374 x
<< "joystick" << i
<< "button" << k
;
375 joybutton
[num
] = new keygroup(x
.str(), keygroup::KT_KEY
);
377 for(int k
= 0; k
< SDL_JoystickNumHats(j
); k
++) {
378 unsigned num
= 256 * i
+ k
;
379 std::ostringstream x
;
380 x
<< "joystick" << i
<< "hat" << k
;
381 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
)
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 #ifndef SDL_NO_JOYSTICK
433 } else if(e
->type
== SDL_JOYAXISMOTION
) {
434 unsigned num
= static_cast<unsigned>(e
->jaxis
.which
) * 256 +
435 static_cast<unsigned>(e
->jaxis
.axis
);
436 if(joyaxis
.count(num
))
437 joyaxis
[num
]->set_position(e
->jaxis
.value
, modifiers
);
438 } else if(e
->type
== SDL_JOYHATMOTION
) {
439 unsigned num
= static_cast<unsigned>(e
->jhat
.which
) * 256 +
440 static_cast<unsigned>(e
->jhat
.hat
);
442 if(e
->jhat
.value
& SDL_HAT_UP
)
444 if(e
->jhat
.value
& SDL_HAT_RIGHT
)
446 if(e
->jhat
.value
& SDL_HAT_DOWN
)
448 if(e
->jhat
.value
& SDL_HAT_LEFT
)
450 if(joyhat
.count(num
))
451 joyhat
[num
]->set_position(v
, modifiers
);
452 } else if(e
->type
== SDL_JOYBUTTONDOWN
|| e
->type
== SDL_JOYBUTTONUP
) {
453 unsigned num
= static_cast<unsigned>(e
->jbutton
.which
) * 256 +
454 static_cast<unsigned>(e
->jbutton
.button
);
455 if(joybutton
.count(num
))
456 joybutton
[num
]->set_position((e
->type
== SDL_JOYBUTTONDOWN
), modifiers
);
461 window::modal_message(h
.keys(), false);
462 keygroup::set_exclusive_key_listener(NULL
);
467 extern uint32_t fontdata
[];
471 bool SDL_initialized
= false;
472 uint32_t mouse_mask
= 0;
475 uint32_t vc_hscl
= 1;
476 uint32_t vc_vscl
= 1;
479 bool modal_return_flag
;
480 bool delayed_close_flag
;
482 std::string command_buf
;
483 bool command_overwrite
;
484 size_t command_cursor
;
485 unsigned old_screen_w
;
486 unsigned old_screen_h
;
488 std::map
<std::string
, std::string
> emustatus
;
489 std::map
<uint64_t, std::string
> messagebuffer
;
490 uint64_t messagebuffer_next_seq
;
491 uint64_t messagebuffer_first_seq
;
492 uint64_t messagebuffer_first_show
;
494 uint32_t maxmessages
;
495 std::list
<std::string
> commandhistory
;
496 std::list
<std::string
>::iterator commandhistory_itr
;
497 screen
* current_screen
;
499 std::pair
<uint32_t, uint32_t> current_windowsize
;
501 uint64_t last_ui_update
;
502 bool screen_is_dirty
;
503 std::ofstream system_log
;
504 SDL_keysym autorepeating_key
;
505 unsigned autorepeat_phase
= 0;
506 unsigned autorepeat_timecounter
= 0;
507 numeric_setting
autorepeat_first("autorepeat-first-delay", 1, 999999999, 15);
508 numeric_setting
autorepeat_subsequent("autorepeat-subsequent-delay", 1, 999999999, 4);
511 void poll_inputs_internal() throw(std::bad_alloc
);
517 state
= WINSTATE_IDENTIFY
;
518 window::message("Press key to identify.");
519 window::notify_screen_update();
520 poll_inputs_internal();
523 std::string
decode_string(std::string e
)
526 for(size_t i
= 0; i
< e
.length(); i
+= 4) {
528 uint32_t c1
= e
[i
] - 33;
529 uint32_t c2
= e
[i
+ 1] - 33;
530 uint32_t c3
= e
[i
+ 2] - 33;
531 uint32_t c4
= e
[i
+ 3] - 33;
532 uint32_t c
= (c1
<< 18) | (c2
<< 12) | (c3
<< 6) | c4
;
535 } else if(c
< 0x800) {
536 tmp
[0] = 0xC0 | (c
>> 6);
537 tmp
[1] = 0x80 | (c
& 0x3F);
538 } else if(c
< 0x10000) {
539 tmp
[0] = 0xE0 | (c
>> 12);
540 tmp
[1] = 0x80 | ((c
>> 6) & 0x3F);
541 tmp
[2] = 0x80 | (c
& 0x3F);
543 tmp
[0] = 0xF0 | (c
>> 18);
544 tmp
[1] = 0x80 | ((c
>> 12) & 0x3F);
545 tmp
[2] = 0x80 | ((c
>> 6) & 0x3F);
546 tmp
[3] = 0x80 | (c
& 0x3F);
553 void draw_rectangle(uint8_t* data
, uint32_t pitch
, uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
554 uint32_t color
, uint32_t thickness
)
556 for(uint32_t i
= x1
; i
< x2
; i
++)
557 for(uint32_t j
= 0; j
< thickness
; j
++) {
558 reinterpret_cast<uint32_t*>(data
+ pitch
* (y1
+ j
))[i
] = color
;
559 reinterpret_cast<uint32_t*>(data
+ pitch
* (y2
- 1 - j
))[i
] = color
;
561 for(uint32_t i
= y1
; i
< y2
; i
++)
562 for(uint32_t j
= 0; j
< thickness
; j
++) {
563 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x1
+ j
] = color
;
564 reinterpret_cast<uint32_t*>(data
+ pitch
* i
)[x2
- 1 - j
] = color
;
568 std::vector
<uint32_t> decode_utf8(std::string s
)
570 std::vector
<uint32_t> ret
;
571 for(auto i
= s
.begin(); i
!= s
.end(); i
++) {
572 uint32_t j
= static_cast<uint8_t>(*i
);
578 uint32_t j2
= static_cast<uint8_t>(*(++i
));
579 ret
.push_back((j
- 192) * 64 + (j2
- 128));
581 uint32_t j2
= static_cast<uint8_t>(*(++i
));
582 uint32_t j3
= static_cast<uint8_t>(*(++i
));
583 ret
.push_back((j
- 224) * 4096 + (j2
- 128) * 64 + (j3
- 128));
585 uint32_t j2
= static_cast<uint8_t>(*(++i
));
586 uint32_t j3
= static_cast<uint8_t>(*(++i
));
587 uint32_t j4
= static_cast<uint8_t>(*(++i
));
588 ret
.push_back((j
- 240) * 262144 + (j2
- 128) * 4096 + (j3
- 128) * 64 + (j4
- 128));
594 void draw_string(uint8_t* base
, uint32_t pitch
, std::vector
<uint32_t> s
, uint32_t x
, uint32_t y
,
595 uint32_t maxwidth
, uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
597 base
+= y
* static_cast<size_t>(pitch
) + 4 * x
;
602 uint32_t old_x
= pos_x
;
603 uint32_t curstart
= 16;
604 if(c
== hilite_pos
&& hilite_mode
== 1)
606 if(c
== hilite_pos
&& hilite_mode
== 2)
608 auto g
= find_glyph(si
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
613 for(unsigned j
= 0; j
< 16; j
++) {
614 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
615 for(unsigned i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++)
616 ptr
[old_x
+ i
] = (j
>= curstart
) ? 0xFFFFFFFFU
: 0;
620 for(unsigned j
= 0; j
< 16; j
++) {
621 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
622 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
623 for(uint32_t i
= 0; i
< g
.first
&& old_x
+ i
< maxwidth
; i
++) {
624 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
626 b
^= (j
>= curstart
);
627 ptr
[old_x
+ i
] = b
? 0xFFFFFFFFU
: 0;
633 for(unsigned j
= 0; j
< 16; j
++) {
634 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ pitch
* j
);
635 uint32_t curstart
= 16;
636 if(c
== hilite_pos
&& hilite_mode
== 1)
638 if(c
== hilite_pos
&& hilite_mode
== 2)
640 for(uint32_t i
= pos_x
; i
< maxwidth
; i
++)
641 ptr
[i
] = ((i
- pos_x
) < 8 && j
>= curstart
) ? 0xFFFFFFFFU
: 0;
645 void draw_string(uint8_t* base
, uint32_t pitch
, std::string s
, uint32_t x
, uint32_t y
, uint32_t maxwidth
,
646 uint32_t hilite_mode
= 0, uint32_t hilite_pos
= 0)
648 draw_string(base
, pitch
, decode_utf8(s
), x
, y
, maxwidth
, hilite_mode
, hilite_pos
);
651 void draw_command(uint8_t* base
, uint32_t pitch
, std::string s
, size_t cursor
, uint32_t x
, uint32_t y
,
652 uint32_t maxwidth
, bool overwrite
)
654 //FIXME, scroll text if too long.
655 uint32_t hilite_mode
= overwrite
? 2 : 1;
656 auto s2
= decode_utf8(s
);
657 draw_string(base
, pitch
, s2
, x
, y
, maxwidth
, hilite_mode
, cursor
);
660 void draw_modal_dialog(SDL_Surface
* surf
, std::string msg
, bool confirm
)
667 msg
= msg
+ "\n\nHit Enter to confirm, Esc to cancel";
669 msg
= msg
+ "\n\nHit Enter or Esc to dismiss";
670 auto s2
= decode_utf8(msg
);
672 auto g
= find_glyph(i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
673 if(pos_x
+ g
.first
> width
)
674 width
= static_cast<uint32_t>(pos_x
+ g
.first
);
675 if(pos_y
+ 16 > static_cast<int32_t>(height
))
676 height
= static_cast<uint32_t>(pos_y
+ 16);
682 if(width
+ 12 >= static_cast<uint32_t>(surf
->w
)) {
687 x1
= (surf
->w
- width
) / 2;
690 if(height
+ 12 >= static_cast<uint32_t>(surf
->h
)) {
695 y1
= (surf
->h
- height
) / 2;
698 for(uint32_t j
= y1
- 6; j
< y2
+ 6; j
++)
699 memset(reinterpret_cast<uint8_t*>(surf
->pixels
) + j
* surf
->pitch
+ 4 * (x1
- 6), 0,
701 uint32_t bordercolor
= (0xFF << surf
->format
->Rshift
) | (0x80 << surf
->format
->Gshift
);
702 draw_rectangle(reinterpret_cast<uint8_t*>(surf
->pixels
), surf
->pitch
, x1
- 4, y1
- 4, x2
+ 4, y2
+ 4,
710 auto g
= find_glyph(i
, pos_x
, pos_y
, 0, pos_x
, pos_y
);
711 if(static_cast<uint32_t>(pos_y
) > height
)
713 uint8_t* base
= reinterpret_cast<uint8_t*>(surf
->pixels
) + (y1
+ oy
) * surf
->pitch
+
717 for(unsigned j
= 0; j
< 16; j
++) {
718 uint32_t* ptr
= reinterpret_cast<uint32_t*>(base
+ surf
->pitch
* j
);
719 uint32_t dataword
= fontdata
[g
.second
+ j
/ 4];
720 for(uint32_t i
= 0; i
< g
.first
&& (ox
+ i
) < width
; i
++) {
721 bool b
= (((dataword
>> (31 - (j
% (32 / g
.first
)) * g
.first
- i
)) &
723 ptr
[i
] = b
? bordercolor
: 0;
730 void do_keyboard_command_edit(SDL_keysym k
)
732 //These are not command edit!
733 if(k
.sym
== SDLK_ESCAPE
)
735 if(k
.sym
== SDLK_RETURN
)
737 if(k
.sym
== SDLK_KP_ENTER
)
739 //Map keys a bit if numlock is off.
740 if((k
.mod
& KMOD_NUM
) == 0) {
742 case SDLK_KP0
: k
.sym
= SDLK_INSERT
; break;
743 case SDLK_KP1
: k
.sym
= SDLK_END
; break;
744 case SDLK_KP2
: k
.sym
= SDLK_DOWN
; break;
745 case SDLK_KP3
: k
.sym
= SDLK_PAGEDOWN
; break;
746 case SDLK_KP4
: k
.sym
= SDLK_LEFT
; break;
747 case SDLK_KP5
: return;
748 case SDLK_KP6
: k
.sym
= SDLK_RIGHT
; break;
749 case SDLK_KP7
: k
.sym
= SDLK_HOME
; break;
750 case SDLK_KP8
: k
.sym
= SDLK_UP
; break;
751 case SDLK_KP9
: k
.sym
= SDLK_PAGEUP
; break;
752 case SDLK_KP_PERIOD
: k
.sym
= SDLK_DELETE
; break;
757 //Special editing operations.
760 command_overwrite
= !command_overwrite
;
761 window::notify_screen_update();
764 command_cursor
= command_buf
.length();
765 window::notify_screen_update();
769 if(commandhistory_itr
!= commandhistory
.begin()) {
770 commandhistory_itr
--;
771 command_buf
= *commandhistory_itr
;
772 if(command_cursor
> command_buf
.length())
773 command_cursor
= command_buf
.length();
775 window::notify_screen_update();
778 command_cursor
= (command_cursor
> 0) ? (command_cursor
- 4) : 0;
779 window::notify_screen_update();
782 command_cursor
= (command_cursor
< command_buf
.length()) ? (command_cursor
+ 4) :
783 command_buf
.length();
784 window::notify_screen_update();
788 window::notify_screen_update();
792 auto tmp
= commandhistory_itr
;
793 if(++tmp
!= commandhistory
.end()) {
794 commandhistory_itr
++;
795 command_buf
= *commandhistory_itr
;
796 if(command_cursor
> command_buf
.length())
797 command_cursor
= command_buf
.length();
799 window::notify_screen_update();
803 if(command_cursor
< command_buf
.length())
804 command_buf
= command_buf
.substr(0, command_cursor
) +
805 command_buf
.substr(command_cursor
+ 4);
806 window::notify_screen_update();
807 *commandhistory_itr
= command_buf
;
810 if(command_cursor
> 0) {
811 command_buf
= command_buf
.substr(0, command_cursor
- 4) +
812 command_buf
.substr(command_cursor
);
815 window::notify_screen_update();
816 *commandhistory_itr
= command_buf
;
822 //Not a special editing operation, insert/overwrite a character.
823 uint32_t code
= k
.unicode
;
826 uint8_t c1
= 33 + ((code
>> 18) & 0x3F);
827 uint8_t c2
= 33 + ((code
>> 12) & 0x3F);
828 uint8_t c3
= 33 + ((code
>> 6) & 0x3F);
829 uint8_t c4
= 33 + (code
& 0x3F);
830 if(command_overwrite
&& command_cursor
< command_buf
.length()) {
831 command_buf
[command_cursor
] = c1
;
832 command_buf
[command_cursor
+ 1] = c2
;
833 command_buf
[command_cursor
+ 2] = c3
;
834 command_buf
[command_cursor
+ 3] = c4
;
837 std::string foo
= " ";
842 command_buf
= command_buf
.substr(0, command_cursor
) + foo
+ command_buf
.substr(command_cursor
);
845 *commandhistory_itr
= command_buf
;
846 window::notify_screen_update();
849 void do_event(SDL_Event
& e
) throw(std::bad_alloc
)
852 alarm(WATCHDOG_TIMEOUT
);
854 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
&& e
.key
.keysym
.mod
== (KMOD_LCTRL
|
857 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
859 window::notify_screen_update();
862 if(e
.type
== SDL_ACTIVEEVENT
&& e
.active
.gain
&& e
.active
.state
== SDL_APPACTIVE
) {
863 window::notify_screen_update();
866 if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_KEYUP
)
867 key
= e
.key
.keysym
.sym
;
869 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_IDENTIFY
)
871 if(e
.type
== SDL_QUIT
&& state
== WINSTATE_MODAL
) {
872 delayed_close_flag
= true;
875 if(e
.type
== SDL_QUIT
) {
876 window_callback::do_close();
877 state
= WINSTATE_NORMAL
;
882 case WINSTATE_NORMAL
:
883 if(e
.type
== SDL_MOUSEBUTTONDOWN
|| e
.type
== SDL_MOUSEBUTTONUP
) {
884 int32_t xc
= e
.button
.x
;
885 int32_t yc
= e
.button
.y
;
886 xc
= (xc
- 6 - vc_xoffset
) / vc_hscl
;
887 yc
= (yc
- 6 - vc_yoffset
) / vc_vscl
;
888 if(e
.button
.button
== SDL_BUTTON_LEFT
) {
889 if(e
.button
.state
== SDL_PRESSED
)
894 if(e
.button
.button
== SDL_BUTTON_MIDDLE
) {
895 if(e
.button
.state
== SDL_PRESSED
)
900 if(e
.button
.button
== SDL_BUTTON_RIGHT
) {
901 if(e
.button
.state
== SDL_PRESSED
)
906 window_callback::do_click(xc
, yc
, mouse_mask
);
908 if(e
.type
== SDL_KEYDOWN
&& key
== SDLK_ESCAPE
)
910 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
911 state
= WINSTATE_COMMAND
;
914 commandhistory
.push_front("");
915 if(commandhistory
.size() > MAXHISTORY
)
916 commandhistory
.pop_back();
917 commandhistory_itr
= commandhistory
.begin();
918 window::notify_screen_update();
919 poll_inputs_internal();
922 process_input_event(&e
, false);
925 //Send the key and eat it (prevent input from getting confused).
926 keygroup::set_exclusive_key_listener(&keyeater
);
927 process_input_event(&e
, false),
928 keygroup::set_exclusive_key_listener(NULL
);
929 if(e
.type
== SDL_KEYUP
&& key
== SDLK_ESCAPE
) {
930 state
= WINSTATE_NORMAL
;
932 modal_return_flag
= true;
934 window::notify_screen_update(true);
937 if(e
.type
== SDL_KEYUP
&& (key
== SDLK_RETURN
|| key
== SDLK_KP_ENTER
)) {
938 state
= WINSTATE_NORMAL
;
939 modal_return_flag
= true;
941 window::notify_screen_update(true);
945 case WINSTATE_COMMAND
:
946 //Send the key and eat it (prevent input from getting confused).
947 keygroup::set_exclusive_key_listener(&keyeater
);
948 process_input_event(&e
, false),
949 keygroup::set_exclusive_key_listener(NULL
);
950 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
) {
951 state
= WINSTATE_NORMAL
;
953 window::notify_screen_update();
954 if(commandhistory
.front() == "")
955 commandhistory
.pop_front();
958 if(e
.type
== SDL_KEYUP
&& (e
.key
.keysym
.sym
== SDLK_RETURN
||
959 e
.key
.keysym
.sym
== SDLK_KP_ENTER
)) {
960 state
= WINSTATE_NORMAL
;
961 if(commandhistory
.front() == "")
962 commandhistory
.pop_front();
963 command::invokeC(decode_string(command_buf
));
965 window::notify_screen_update();
966 autorepeat_phase
= 0;
969 if(e
.type
== SDL_KEYDOWN
) {
970 autorepeating_key
= e
.key
.keysym
;
971 autorepeat_phase
= 1;
972 autorepeat_timecounter
= 0;
973 do_keyboard_command_edit(e
.key
.keysym
);
974 } else if(e
.type
== SDL_KEYUP
) {
975 autorepeat_phase
= 0;
977 if(e
.type
== SDL_USEREVENT
&& e
.user
.code
== 0) {
978 autorepeat_timecounter
++;
979 if(!autorepeat_phase
)
981 unsigned timeout
= (autorepeat_phase
== 1) ? autorepeat_first
: autorepeat_subsequent
;
982 if(autorepeat_timecounter
>= timeout
) {
983 do_keyboard_command_edit(autorepeating_key
);
984 autorepeat_timecounter
= 0;
985 autorepeat_phase
= 2;
989 case WINSTATE_IDENTIFY
:
990 process_input_event(&e
, true);
998 SDL_initialized
= true;
1000 signal(SIGALRM
, sigalrm_handler
);
1001 alarm(WATCHDOG_TIMEOUT
);
1004 system_log
.open("lsnes.log", std::ios_base::out
| std::ios_base::app
);
1005 time_t curtime
= __real_time(NULL
);
1006 struct tm
* tm
= localtime(&curtime
);
1008 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1009 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1010 system_log
<< "lsnes started at " << buffer
<< std::endl
;
1011 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1013 SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
| SDL_INIT_JOYSTICK
| SDL_INIT_TIMER
);
1014 SDL_EnableUNICODE(true);
1016 tid
= SDL_AddTimer(MIN_UPDATE_TIME
/ 1000 + 1, timer_cb
, NULL
);
1018 state
= WINSTATE_NORMAL
;
1019 current_screen
= NULL
;
1020 pause_active
= false;
1022 command_overwrite
= false;
1025 modal_return_flag
= false;
1026 delayed_close_flag
= false;
1027 messagebuffer_next_seq
= 0;
1028 messagebuffer_first_seq
= 0;
1029 messagebuffer_first_show
= 0;
1030 console_mode
= false;
1031 maxmessages
= MAXMESSAGES
;
1033 window::notify_screen_update();
1034 std::string windowname
= "lsnes-" + lsnes_version
+ "[" + bsnes_core_version
+ "]";
1035 SDL_WM_SetCaption(windowname
.c_str(), "lsnes");
1040 void graphics_quit()
1042 time_t curtime
= time(NULL
);
1043 struct tm
* tm
= localtime(&curtime
);
1045 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1046 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1047 system_log
<< "lsnes shutting down at " << buffer
<< std::endl
;
1048 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1051 SDL_RemoveTimer(tid
);
1055 SDL_initialized
= false;
1058 bool window::modal_message(const std::string
& msg
, bool confirm
) throw(std::bad_alloc
)
1060 modconfirm
= confirm
;
1062 state
= WINSTATE_MODAL
;
1063 notify_screen_update();
1064 poll_inputs_internal();
1065 bool ret
= modconfirm
;
1066 if(delayed_close_flag
) {
1067 delayed_close_flag
= false;
1068 window_callback::do_close();
1073 void window::message(const std::string
& msg
) throw(std::bad_alloc
)
1075 std::string msg2
= msg
;
1076 bool locked_mode
= (messagebuffer_next_seq
- messagebuffer_first_show
<= maxmessages
) ;
1078 size_t s
= msg2
.find_first_of("\n");
1080 if(s
>= msg2
.length()) {
1081 if(SDL_initialized
) {
1082 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
);
1083 system_log
<< forlog
<< std::endl
;
1085 std::cerr
<< msg2
<< std::endl
;
1088 if(SDL_initialized
) {
1089 messagebuffer
[messagebuffer_next_seq
++] = (forlog
= msg2
.substr(0, s
));
1090 system_log
<< forlog
<< std::endl
;
1092 std::cerr
<< msg2
.substr(0, s
) << std::endl
;
1093 msg2
= msg2
.substr(s
+ 1);
1097 if(locked_mode
&& messagebuffer_first_show
+ maxmessages
< messagebuffer_next_seq
)
1098 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1100 while(messagebuffer
.size() > MSGHISTORY
) {
1101 messagebuffer
.erase(messagebuffer_first_seq
++);
1102 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1103 messagebuffer_first_show
= messagebuffer_first_seq
;
1105 notify_screen_update();
1108 void window::set_main_surface(screen
& scr
) throw()
1110 current_screen
= &scr
;
1111 notify_screen_update(true);
1116 bool is_time_for_screen_update(bool full
)
1118 uint64_t curtime
= get_utime();
1119 //Always do full updates.
1120 if(!full
&& last_ui_update
< curtime
&& last_ui_update
+ MIN_UPDATE_TIME
> curtime
) {
1121 screen_is_dirty
= true;
1124 last_ui_update
= curtime
;
1125 screen_is_dirty
= false;
1129 std::pair
<uint32_t, uint32_t> compute_screen_size(uint32_t width
, uint32_t height
)
1135 return std::make_pair(width
, height
);
1138 std::pair
<uint32_t, uint32_t> compute_window_size(uint32_t width
, uint32_t height
)
1140 auto g
= compute_screen_size(width
, height
);
1141 uint32_t win_w
= ((g
.first
+ 15) >> 4 << 4) + 278;
1142 uint32_t win_h
= g
.second
+ MAXMESSAGES
* 16 + 48;
1143 return std::make_pair(win_w
, win_h
);
1149 std::ostringstream y
;
1150 y
<< get_framerate();
1151 emustatus
["FPS"] = y
.str();
1156 void redraw_borders(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
,
1157 std::pair
<uint32_t, uint32_t> windowsize
)
1159 //Blank the screen and draw borders.
1160 memset(swsurf
->pixels
, 0, windowsize
.second
* swsurf
->pitch
);
1161 uint32_t bordercolor
= 0xFF << (swsurf
->format
->Gshift
);
1162 uint32_t msgbox_min_x
= 2;
1163 uint32_t msgbox_min_y
= 2;
1164 uint32_t msgbox_max_x
= windowsize
.first
- 2;
1165 uint32_t msgbox_max_y
= windowsize
.second
- 28;
1166 uint32_t cmdbox_min_x
= 2;
1167 uint32_t cmdbox_max_x
= windowsize
.first
- 2;
1168 uint32_t cmdbox_min_y
= windowsize
.second
- 26;
1169 uint32_t cmdbox_max_y
= windowsize
.second
- 2;
1171 uint32_t scrbox_min_x
= 2;
1172 uint32_t scrbox_max_x
= screensize
.first
+ 10;
1173 uint32_t scrbox_min_y
= 2;
1174 uint32_t scrbox_max_y
= screensize
.second
+ 10;
1175 uint32_t stsbox_min_x
= screensize
.first
+ 12;
1176 uint32_t stsbox_max_x
= windowsize
.first
- 2;
1177 uint32_t stsbox_min_y
= 2;
1178 uint32_t stsbox_max_y
= screensize
.second
+ 10;
1179 msgbox_min_y
= screensize
.second
+ 12;
1180 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, scrbox_min_x
,
1181 scrbox_min_y
, scrbox_max_x
, scrbox_max_y
, bordercolor
, 2);
1182 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, stsbox_min_x
,
1183 stsbox_min_y
, stsbox_max_x
, stsbox_max_y
, bordercolor
, 2);
1185 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, msgbox_min_x
,
1186 msgbox_min_y
, msgbox_max_x
, msgbox_max_y
, bordercolor
, 2);
1187 draw_rectangle(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, cmdbox_min_x
,
1188 cmdbox_min_y
, cmdbox_max_x
, cmdbox_max_y
, bordercolor
, 2);
1191 void draw_main_screen(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
)
1193 uint32_t cw
= current_screen
? current_screen
->width
: 0;
1194 uint32_t ch
= current_screen
? current_screen
->height
: 0;
1195 //Blank parts not drawn.
1196 for(uint32_t i
= 6; i
< ch
+ 6; i
++)
1197 memset(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + i
* swsurf
->pitch
+ 24 + 4 * cw
, 0,
1198 4 * (screensize
.first
- cw
));
1199 for(uint32_t i
= ch
+ 6; i
< screensize
.second
+ 6; i
++)
1200 memset(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + i
* swsurf
->pitch
+ 24, 0,
1201 4 * screensize
.first
);
1202 if(current_screen
) {
1203 for(uint32_t i
= 0; i
< ch
; i
++)
1204 memcpy(reinterpret_cast<uint8_t*>(swsurf
->pixels
) + (i
+ 6) * swsurf
->pitch
+ 24,
1205 reinterpret_cast<uint8_t*>(current_screen
->memory
) + current_screen
->pitch
* i
,
1210 void draw_status_area(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
)
1212 uint32_t status_x
= screensize
.first
+ 16;
1213 uint32_t status_y
= 6;
1214 for(auto i
: emustatus
) {
1215 std::string msg
= i
.first
+ " " + i
.second
;
1216 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, msg
, status_x
, status_y
,
1220 while(status_y
- 6 < screensize
.second
/ 16 * 16) {
1221 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "", status_x
, status_y
,
1227 void draw_messages(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> screensize
,
1228 std::pair
<uint32_t, uint32_t> windowsize
)
1232 message_y
= screensize
.second
+ 16;
1235 for(size_t j
= 0; j
< maxmessages
; j
++)
1237 std::ostringstream o
;
1238 if(messagebuffer_first_show
+ j
< messagebuffer_next_seq
)
1239 o
<< (messagebuffer_first_show
+ j
+ 1) << ": "
1240 << messagebuffer
[messagebuffer_first_show
+ j
];
1241 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, o
.str(), 6,
1242 message_y
+ 16 * j
, windowsize
.first
- 12);
1245 if(messagebuffer_next_seq
- messagebuffer_first_show
> maxmessages
)
1247 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "--More--",
1248 windowsize
.first
- 76, message_y
+ 16 * maxmessages
- 16, 64);
1253 void draw_command(SDL_Surface
* swsurf
, std::pair
<uint32_t, uint32_t> windowsize
)
1255 uint32_t command_y
= windowsize
.second
- 22;
1257 std::string command_showas
= decode_string(command_buf
);
1258 if(state
== WINSTATE_COMMAND
)
1259 draw_command(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, command_showas
,
1260 command_cursor
/ 4, 6, command_y
, windowsize
.first
- 12, command_overwrite
);
1262 draw_string(reinterpret_cast<uint8_t*>(swsurf
->pixels
), swsurf
->pitch
, "", 6,
1263 command_y
, windowsize
.first
- 12);
1269 void window::notify_screen_update(bool full
) throw()
1271 uint64_t tnow
= get_utime();
1272 bool resize_screen
= false;
1273 if(!is_time_for_screen_update(full
)) {
1276 auto windowsize
= compute_window_size(current_screen
? current_screen
->width
: 0, current_screen
?
1277 current_screen
->height
: 0);
1278 auto screensize
= compute_screen_size(current_screen
? current_screen
->width
: 0, current_screen
?
1279 current_screen
->height
: 0);
1283 if(!hwsurf
|| windowsize
!= current_windowsize
) {
1284 //Create/Resize the window.
1285 SDL_Surface
* hwsurf2
= SDL_SetVideoMode(windowsize
.first
, windowsize
.second
, 32, SDL_SWSURFACE
);
1287 //We are in too fucked up state to even print error as message.
1288 std::cout
<< "PANIC: Can't create/resize window: " << SDL_GetError() << std::endl
;
1293 current_windowsize
= windowsize
;
1296 current_screen
->set_palette(hwsurf
->format
->Rshift
, hwsurf
->format
->Gshift
, hwsurf
->format
->Bshift
);
1297 SDL_LockSurface(hwsurf
);
1299 redraw_borders(hwsurf
, screensize
, windowsize
);
1301 draw_main_screen(hwsurf
, screensize
);
1302 draw_status_area(hwsurf
, screensize
);
1304 draw_messages(hwsurf
, screensize
, windowsize
);
1305 draw_command(hwsurf
, windowsize
);
1307 //Draw modal dialog.
1308 if(state
== WINSTATE_MODAL
)
1310 draw_modal_dialog(hwsurf
, modmsg
, modconfirm
);
1313 SDL_UnlockSurface(hwsurf
);
1314 //SDL_BlitSurface(swsurf, NULL, hwsurf, NULL);
1315 SDL_UpdateRect(hwsurf
, 0, 0, 0, 0);
1316 in_paint_time
+= (get_utime() - tnow
);
1319 void poll_inputs_internal() throw(std::bad_alloc
)
1322 while(state
!= WINSTATE_NORMAL
) {
1324 if(SDL_PollEvent(&e
))
1327 if(delayed_close_flag
) {
1328 state
= WINSTATE_NORMAL
;
1334 void window::poll_inputs() throw(std::bad_alloc
)
1338 assert(state
== WINSTATE_NORMAL
);
1340 if(!pause_active
&& !SDL_PollEvent(&e
))
1342 else if(!pause_active
)
1344 else if(SDL_PollEvent(&e
))
1351 std::map
<std::string
, std::string
>& window::get_emustatus() throw()
1356 void window::paused(bool enable
) throw()
1358 pause_active
= enable
;
1359 notify_screen_update();
1364 function_ptr_command
<> identify_key("identify-key", "Identify a key",
1365 "Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
1366 []() throw(std::bad_alloc
, std::runtime_error
) {
1370 function_ptr_command
<> scroll_up("scroll-up", "Scroll messages a page up",
1371 "Syntax: scroll-up\nScrolls message console backward one page.\n",
1372 []() throw(std::bad_alloc
, std::runtime_error
) {
1373 if(messagebuffer_first_show
> maxmessages
)
1374 messagebuffer_first_show
-= maxmessages
;
1376 messagebuffer_first_show
= 0;
1377 if(messagebuffer_first_show
< messagebuffer_first_seq
)
1378 messagebuffer_first_show
= messagebuffer_first_seq
;
1379 window::notify_screen_update();
1382 function_ptr_command
<> scroll_fullup("scroll-fullup", "Scroll messages to beginning",
1383 "Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
1384 []() throw(std::bad_alloc
, std::runtime_error
) {
1385 messagebuffer_first_show
= messagebuffer_first_seq
;
1386 window::notify_screen_update();
1389 function_ptr_command
<> scroll_fulldown("scroll-fulldown", "Scroll messages to end",
1390 "Syntax: scroll-fulldown\nScrolls message console to its end.\n",
1391 []() throw(std::bad_alloc
, std::runtime_error
) {
1392 if(messagebuffer_next_seq
< maxmessages
)
1393 messagebuffer_first_show
= 0;
1395 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1396 window::notify_screen_update();
1399 function_ptr_command
<> scrolldown("scroll-down", "Scroll messages a page down",
1400 "Syntax: scroll-up\nScrolls message console forward one page.\n",
1401 []() throw(std::bad_alloc
, std::runtime_error
) {
1402 messagebuffer_first_show
+= maxmessages
;
1403 if(messagebuffer_next_seq
< maxmessages
)
1404 messagebuffer_first_show
= 0;
1405 else if(messagebuffer_next_seq
< messagebuffer_first_show
+ maxmessages
)
1406 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1407 window::notify_screen_update();
1410 function_ptr_command
<> toggle_console("toggle-console", "Toggle console between small and full window",
1411 "Syntax: toggle-console\nToggles console between small and large.\n",
1412 []() throw(std::bad_alloc
, std::runtime_error
) {
1413 console_mode
= !console_mode
;
1415 maxmessages
= hwsurf
? (hwsurf
->h
- 38) / 16 : 36;
1417 maxmessages
= MAXMESSAGES
;
1418 if(messagebuffer_next_seq
< maxmessages
)
1419 messagebuffer_first_show
= 0;
1421 messagebuffer_first_show
= messagebuffer_next_seq
- maxmessages
;
1422 window::notify_screen_update(true);
1426 void window::wait_usec(uint64_t usec
) throw(std::bad_alloc
)
1428 wait_canceled
= false;
1429 uint64_t end_at
= get_utime() + usec
;
1430 while(!wait_canceled
) {
1433 while(SDL_PollEvent(&e
))
1435 uint64_t curtime
= get_utime();
1436 if(curtime
> end_at
|| wait_canceled
)
1438 if(end_at
> curtime
+ 10000)
1441 ::wait_usec(end_at
- curtime
);
1443 wait_canceled
= false;
1446 void window::fatal_error() throw()
1449 message("PANIC: Cannot continue, press ESC or close window to exit.");
1451 notify_screen_update(true);
1456 time_t curtime
= time(NULL
);
1457 struct tm
* tm
= localtime(&curtime
);
1459 strftime(buffer
, 1023, "%Y-%m-%d %H:%M:%S %Z", tm
);
1460 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1461 system_log
<< "lsnes paniced at " << buffer
<< std::endl
;
1462 system_log
<< "-----------------------------------------------------------------------" << std::endl
;
1466 if(SDL_WaitEvent(&e
)) {
1467 if(e
.type
== SDL_QUIT
)
1469 if(e
.type
== SDL_KEYUP
&& e
.key
.keysym
.sym
== SDLK_ESCAPE
)
1475 void window::cancel_wait() throw()
1477 wait_canceled
= true;
1480 void window::set_window_compensation(uint32_t xoffset
, uint32_t yoffset
, uint32_t hscl
, uint32_t vscl
)
1482 vc_xoffset
= xoffset
;
1483 vc_yoffset
= yoffset
;
1488 #ifndef SDL_NO_JOYSTICK
1489 void poll_joysticks()
1491 //We poll it in event loop for SDL.
1494 const char* joystick_plugin_name
= "SDL joystick plugin";
1497 const char* graphics_plugin_name
= "SDL graphics plugin";