Add VCS links
[debian-dgen.git] / sdl / sdl.cpp
blobf74e42d2d03cd690041e45668f1146d754d0e92d
1 /**
2 * SDL interface
3 */
5 #ifdef __MINGW32__
6 #undef __STRICT_ANSI__
7 #endif
9 #include <sys/time.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <strings.h>
15 #include <stdarg.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <ctype.h>
19 #include <assert.h>
20 #include <SDL.h>
21 #include <SDL_audio.h>
23 #ifdef WITH_OPENGL
24 # include <SDL_opengl.h>
25 #endif
27 #ifdef WITH_THREADS
28 #include <SDL_thread.h>
29 #endif
31 #ifdef HAVE_MEMCPY_H
32 #include "memcpy.h"
33 #endif
34 #include "md.h"
35 #include "rc.h"
36 #include "rc-vars.h"
37 #include "pd.h"
38 #include "pd-defs.h"
39 #include "font.h"
40 #include "system.h"
41 #include "prompt.h"
42 #include "romload.h"
43 #include "splash.h"
45 #ifdef WITH_HQX
46 #define HQX_NO_UINT24
47 #include "hqx.h"
48 #endif
50 #ifdef WITH_SCALE2X
51 extern "C" {
52 #include "scalebit.h"
54 #endif
56 /// Number of microseconds to sustain messages
57 #define MESSAGE_LIFE 3000000
59 static void pd_message_process(void);
60 static size_t pd_message_write(const char *msg, size_t len, unsigned int mark);
61 static size_t pd_message_display(const char *msg, size_t len,
62 unsigned int mark, bool update);
63 static void pd_message_postpone(const char *msg);
64 static void pd_message_cursor(unsigned int mark, const char *msg, ...);
66 /// Generic type for supported colour depths.
67 typedef union {
68 uint8_t *u8;
69 uint32_t *u32;
70 uint24_t *u24;
71 uint16_t *u16;
72 uint16_t *u15;
73 } bpp_t;
75 #ifdef WITH_OPENGL
77 /// Framebuffer texture.
78 struct texture {
79 unsigned int width; ///< texture width
80 unsigned int height; ///< texture height
81 unsigned int vis_width; ///< visible width
82 unsigned int vis_height; ///< visible height
83 GLuint id; ///< texture identifier
84 GLuint dlist; ///< display list
85 unsigned int u32:1; ///< texture is 32-bit
86 unsigned int linear:1; ///< linear filtering is enabled
87 union {
88 uint16_t *u16;
89 uint32_t *u32;
90 } buf; ///< 16 or 32-bit buffer
93 static void release_texture(struct texture&);
94 static int init_texture(struct screen *);
95 static void update_texture(struct texture&);
97 #endif // WITH_OPENGL
99 struct screen {
100 unsigned int window_width; ///< window width
101 unsigned int window_height; ///< window height
102 unsigned int width; ///< buffer width
103 unsigned int height; ///< buffer height
104 unsigned int bpp; ///< bits per pixel
105 unsigned int Bpp; ///< bytes per pixel
106 unsigned int x_scale; ///< horizontal scale factor
107 unsigned int y_scale; ///< vertical scale factor
108 unsigned int info_height; ///< message bar height (included in height)
109 bpp_t buf; ///< generic pointer to pixel data
110 unsigned int pitch; ///< number of bytes per line in buf
111 SDL_Surface *surface; ///< SDL surface
112 unsigned int want_fullscreen:1; ///< want fullscreen
113 unsigned int is_fullscreen:1; ///< fullscreen enabled
114 #ifdef WITH_OPENGL
115 struct texture texture; ///< OpenGL texture data
116 unsigned int want_opengl:1; ///< want OpenGL
117 unsigned int is_opengl:1; ///< OpenGL enabled
118 #endif
119 #ifdef WITH_THREADS
120 unsigned int want_thread:1; ///< want updates from a separate thread
121 unsigned int is_thread:1; ///< thread is present
122 SDL_Thread *thread; ///< thread itself
123 SDL_mutex *lock; ///< lock for updates
124 SDL_cond *cond; ///< condition variable to signal updates
125 #endif
126 SDL_Color color[64]; ///< SDL colors for 8bpp modes
129 static struct screen screen;
131 static struct {
132 const unsigned int width; ///< 320
133 unsigned int height; ///< 224 or 240 (NTSC_VBLANK or PAL_VBLANK)
134 unsigned int hz; ///< refresh rate
135 unsigned int is_pal: 1; ///< PAL enabled
136 uint8_t palette[256]; ///< palette for 8bpp modes (mdpal)
137 } video = {
138 320, ///< width is always 320
139 NTSC_VBLANK, ///< NTSC height by default
140 NTSC_HZ, ///< 60Hz
141 0, ///< NTSC is enabled
142 { 0 }
146 * Call this before accessing screen.buf.
147 * No syscalls allowed before screen_unlock().
149 static int screen_lock()
151 #ifdef WITH_THREADS
152 if (screen.is_thread) {
153 assert(screen.lock != NULL);
154 SDL_LockMutex(screen.lock);
156 #endif
157 #ifdef WITH_OPENGL
158 if (screen.is_opengl)
159 return 0;
160 #endif
161 if (SDL_MUSTLOCK(screen.surface) == 0)
162 return 0;
163 return SDL_LockSurface(screen.surface);
167 * Call this after accessing screen.buf.
169 static void screen_unlock()
171 #ifdef WITH_THREADS
172 if (screen.is_thread) {
173 assert(screen.lock != NULL);
174 SDL_UnlockMutex(screen.lock);
176 #endif
177 #ifdef WITH_OPENGL
178 if (screen.is_opengl)
179 return;
180 #endif
181 if (SDL_MUSTLOCK(screen.surface) == 0)
182 return;
183 SDL_UnlockSurface(screen.surface);
187 * Do not call this directly, use screen_update() instead.
189 static void screen_update_once()
191 #ifdef WITH_OPENGL
192 if (screen.is_opengl) {
193 update_texture(screen.texture);
194 return;
196 #endif
197 SDL_Flip(screen.surface);
200 #ifdef WITH_THREADS
202 static int screen_update_thread(void *)
204 assert(screen.lock != NULL);
205 assert(screen.cond != NULL);
206 SDL_LockMutex(screen.lock);
207 while (screen.want_thread) {
208 SDL_CondWait(screen.cond, screen.lock);
209 screen_update_once();
211 SDL_UnlockMutex(screen.lock);
212 return 0;
215 static void screen_update_thread_start()
217 DEBUG(("starting thread..."));
218 assert(screen.want_thread);
219 assert(screen.lock == NULL);
220 assert(screen.cond == NULL);
221 assert(screen.thread == NULL);
222 #ifdef WITH_OPENGL
223 if (screen.is_opengl) {
224 DEBUG(("this is not supported when OpenGL is enabled"));
225 return;
227 #endif
228 if ((screen.lock = SDL_CreateMutex()) == NULL) {
229 DEBUG(("unable to create lock"));
230 goto error;
233 if ((screen.cond = SDL_CreateCond()) == NULL) {
234 DEBUG(("unable to create condition variable"));
235 goto error;
237 screen.thread = SDL_CreateThread(screen_update_thread, NULL);
238 if (screen.thread == NULL) {
239 DEBUG(("unable to start thread"));
240 goto error;
242 screen.is_thread = 1;
243 DEBUG(("thread started"));
244 return;
245 error:
246 if (screen.cond != NULL) {
247 SDL_DestroyCond(screen.cond);
248 screen.cond = NULL;
250 if (screen.lock != NULL) {
251 SDL_DestroyMutex(screen.lock);
252 screen.lock = NULL;
256 static void screen_update_thread_stop()
258 if (!screen.is_thread) {
259 assert(screen.thread == NULL);
260 return;
262 DEBUG(("stopping thread..."));
263 assert(screen.thread != NULL);
264 screen.want_thread = 0;
265 SDL_CondSignal(screen.cond);
266 SDL_WaitThread(screen.thread, NULL);
267 screen.thread = NULL;
268 SDL_DestroyCond(screen.cond);
269 screen.cond = NULL;
270 SDL_DestroyMutex(screen.lock);
271 screen.lock = NULL;
272 screen.is_thread = 0;
273 DEBUG(("thread stopped"));
276 #endif // WITH_THREADS
279 * Call this after writing into screen.buf.
281 static void screen_update()
283 #ifdef WITH_THREADS
284 if (screen.is_thread)
285 SDL_CondSignal(screen.cond);
286 else
287 #endif // WITH_THREADS
288 screen_update_once();
292 * Clear screen.
294 static void screen_clear()
296 if ((screen.buf.u8 == NULL) || (screen_lock()))
297 return;
298 memset(screen.buf.u8, 0, (screen.pitch * screen.height));
299 screen_unlock();
302 // Bad hack- extern slot etc. from main.cpp so we can save/load states
303 extern int slot;
304 void md_save(md &megad);
305 void md_load(md &megad);
307 // Define externed variables
308 struct bmap mdscr;
309 unsigned char *mdpal = NULL;
310 struct sndinfo sndi;
311 const char *pd_options =
312 #ifdef WITH_OPENGL
313 "g:"
314 #endif
315 "fX:Y:S:G:";
317 static void mdscr_splash();
319 /// Circular buffer and related functions.
320 typedef struct {
321 size_t i; ///< data start index
322 size_t s; ///< data size
323 size_t size; ///< buffer size
324 union {
325 uint8_t *u8;
326 int16_t *i16;
327 } data; ///< storage
328 } cbuf_t;
331 * Write/copy data into a circular buffer.
332 * @param[in,out] cbuf Destination buffer.
333 * @param[in] src Buffer to copy from.
334 * @param size Size of src.
335 * @return Number of bytes copied.
337 size_t cbuf_write(cbuf_t *cbuf, uint8_t *src, size_t size)
339 size_t j;
340 size_t k;
342 if (size > cbuf->size) {
343 src += (size - cbuf->size);
344 size = cbuf->size;
346 k = (cbuf->size - cbuf->s);
347 j = ((cbuf->i + cbuf->s) % cbuf->size);
348 if (size > k) {
349 cbuf->i = ((cbuf->i + (size - k)) % cbuf->size);
350 cbuf->s = cbuf->size;
352 else
353 cbuf->s += size;
354 k = (cbuf->size - j);
355 if (k >= size) {
356 memcpy(&cbuf->data.u8[j], src, size);
358 else {
359 memcpy(&cbuf->data.u8[j], src, k);
360 memcpy(&cbuf->data.u8[0], &src[k], (size - k));
362 return size;
366 * Read bytes out of a circular buffer.
367 * @param[out] dst Destination buffer.
368 * @param[in,out] cbuf Circular buffer to read from.
369 * @param size Maximum number of bytes to copy to dst.
370 * @return Number of bytes copied.
372 size_t cbuf_read(uint8_t *dst, cbuf_t *cbuf, size_t size)
374 if (size > cbuf->s)
375 size = cbuf->s;
376 if ((cbuf->i + size) > cbuf->size) {
377 size_t k = (cbuf->size - cbuf->i);
379 memcpy(&dst[0], &cbuf->data.u8[(cbuf->i)], k);
380 memcpy(&dst[k], &cbuf->data.u8[0], (size - k));
382 else
383 memcpy(&dst[0], &cbuf->data.u8[(cbuf->i)], size);
384 cbuf->i = ((cbuf->i + size) % cbuf->size);
385 cbuf->s -= size;
386 return size;
389 /// Sound
390 static struct {
391 unsigned int rate; ///< samples rate
392 unsigned int samples; ///< number of samples required by the callback
393 cbuf_t cbuf; ///< circular buffer
394 } sound;
396 /// Messages
397 static struct {
398 unsigned int displayed:1; ///< whether message is currently displayed
399 unsigned long since; ///< since this number of microseconds
400 size_t length; ///< remaining length to display
401 char message[2048]; ///< message
402 } info;
404 /// Prompt
405 static struct {
406 struct prompt status; ///< prompt status
407 char** complete; ///< completion results array
408 unsigned int skip; ///< number of entries to skip in the array
409 unsigned int common; ///< common length of all entries
410 } prompt;
412 /// Prompt return values
413 #define PROMPT_RET_CONT 0x01 ///< waiting for more input
414 #define PROMPT_RET_EXIT 0x02 ///< leave prompt normally
415 #define PROMPT_RET_ERROR 0x04 ///< leave prompt with error
416 #define PROMPT_RET_ENTER 0x10 ///< previous line entered
417 #define PROMPT_RET_MSG 0x80 ///< pd_message() has been used
419 struct prompt_command {
420 const char* name;
421 /// command function pointer
422 int (*cmd)(class md&, unsigned int, const char**);
423 /// completion function shoud complete the last entry in the array
424 char* (*cmpl)(class md&, unsigned int, const char**, unsigned int);
427 // Extra commands usable from prompt.
428 static int prompt_cmd_exit(class md&, unsigned int, const char**);
429 static int prompt_cmd_load(class md&, unsigned int, const char**);
430 static char* prompt_cmpl_load(class md&, unsigned int, const char**,
431 unsigned int);
432 static int prompt_cmd_unload(class md&, unsigned int, const char**);
433 static int prompt_cmd_reset(class md&, unsigned int, const char**);
434 static int prompt_cmd_unbind(class md&, unsigned int, const char**);
435 static char* prompt_cmpl_unbind(class md&, unsigned int, const char**,
436 unsigned int);
437 #ifdef WITH_CTV
438 static int prompt_cmd_filter_push(class md&, unsigned int, const char**);
439 static char* prompt_cmpl_filter_push(class md&, unsigned int, const char**,
440 unsigned int);
441 static int prompt_cmd_filter_pop(class md&, unsigned int, const char**);
442 static int prompt_cmd_filter_none(class md&, unsigned int, const char**);
443 #endif
444 static int prompt_cmd_calibrate(class md&, unsigned int, const char**);
445 static int prompt_cmd_config_load(class md&, unsigned int, const char**);
446 static int prompt_cmd_config_save(class md&, unsigned int, const char**);
447 static char* prompt_cmpl_config_file(class md&, unsigned int, const char**,
448 unsigned int);
449 #ifdef WITH_VGMDUMP
450 static char* prompt_cmpl_vgmdump(class md&, unsigned int, const char**,
451 unsigned int);
452 static int prompt_cmd_vgmdump(class md&, unsigned int, const char**);
453 #endif
456 * List of commands to auto complete.
458 static const struct prompt_command prompt_command[] = {
459 { "quit", prompt_cmd_exit, NULL },
460 { "q", prompt_cmd_exit, NULL },
461 { "exit", prompt_cmd_exit, NULL },
462 { "load", prompt_cmd_load, prompt_cmpl_load },
463 { "open", prompt_cmd_load, prompt_cmpl_load },
464 { "o", prompt_cmd_load, prompt_cmpl_load },
465 { "plug", prompt_cmd_load, prompt_cmpl_load },
466 { "unload", prompt_cmd_unload, NULL },
467 { "close", prompt_cmd_unload, NULL },
468 { "unplug", prompt_cmd_unload, NULL },
469 { "reset", prompt_cmd_reset, NULL },
470 { "unbind", prompt_cmd_unbind, prompt_cmpl_unbind },
471 #ifdef WITH_CTV
472 { "ctv_push", prompt_cmd_filter_push, prompt_cmpl_filter_push },
473 { "ctv_pop", prompt_cmd_filter_pop, NULL },
474 { "ctv_none", prompt_cmd_filter_none, NULL },
475 #endif
476 { "calibrate", prompt_cmd_calibrate, NULL },
477 { "calibrate_js", prompt_cmd_calibrate, NULL }, // deprecated name
478 { "config_load", prompt_cmd_config_load, prompt_cmpl_config_file },
479 { "config_save", prompt_cmd_config_save, prompt_cmpl_config_file },
480 #ifdef WITH_VGMDUMP
481 { "vgmdump", prompt_cmd_vgmdump, prompt_cmpl_vgmdump },
482 #endif
483 { NULL, NULL, NULL }
486 /// Extra commands return values.
487 #define CMD_OK 0x00 ///< command successful
488 #define CMD_EINVAL 0x01 ///< invalid argument
489 #define CMD_FAIL 0x02 ///< command failed
490 #define CMD_ERROR 0x03 ///< fatal error, DGen should exit
491 #define CMD_MSG 0x80 ///< pd_message() has been used
493 /// Stopped flag used by pd_stopped()
494 static int stopped = 0;
496 /// Events handling status.
497 static enum events {
498 STARTED,
499 STOPPED,
500 STOPPED_PROMPT,
501 STOPPED_GAME_GENIE,
502 PROMPT,
503 GAME_GENIE
504 } events = STARTED;
506 static int stop_events(md& megad, enum events status);
507 static void restart_events(md &megad);
509 /// Messages shown whenever events are stopped.
510 static const char stopped_str[] = "STOPPED.";
511 static const char prompt_str[] = ":";
512 static const char game_genie_str[] = "Enter Game Genie/Hex code: ";
514 /// Enable emulation by default.
515 bool pd_freeze = false;
516 static unsigned int pd_freeze_ref = 0;
518 static void freeze(bool toggle)
520 if (toggle == true) {
521 if (!pd_freeze_ref) {
522 assert(pd_freeze == false);
523 pd_freeze = true;
525 pd_freeze_ref++;
527 else {
528 if (pd_freeze_ref) {
529 assert(pd_freeze == true);
530 pd_freeze_ref--;
531 if (!pd_freeze_ref)
532 pd_freeze = false;
534 else
535 assert(pd_freeze == false);
540 * Elapsed time in microseconds.
541 * @return Microseconds.
543 unsigned long pd_usecs(void)
545 struct timeval tv;
547 gettimeofday(&tv, NULL);
548 return (unsigned long)((tv.tv_sec * 1000000) + tv.tv_usec);
552 * Prompt "exit" command handler.
553 * @return Error status to make DGen exit.
555 static int prompt_cmd_exit(class md&, unsigned int, const char**)
557 return (CMD_ERROR | CMD_MSG);
561 * Prompt "load" command handler.
562 * @param md Context.
563 * @param ac Number of arguments in av.
564 * @param av Arguments.
565 * @return Status code.
567 static int prompt_cmd_load(class md& md, unsigned int ac, const char** av)
569 extern int slot;
570 extern void ram_save(class md&);
571 extern void ram_load(class md&);
572 char *s;
574 if (ac != 2)
575 return CMD_EINVAL;
576 s = backslashify((const uint8_t *)av[1], strlen(av[1]), 0, NULL);
577 if (s == NULL)
578 return CMD_FAIL;
579 ram_save(md);
580 if (dgen_autosave) {
581 slot = 0;
582 md_save(md);
584 md.unplug();
585 pd_message("");
586 if (md.load(av[1])) {
587 mdscr_splash();
588 pd_message("Unable to load \"%s\"", s);
589 free(s);
590 return (CMD_FAIL | CMD_MSG);
592 pd_message("Loaded \"%s\"", s);
593 free(s);
594 if (dgen_show_carthead)
595 pd_show_carthead(md);
596 // Initialize like main() does.
597 md.reset();
599 if (!dgen_region) {
600 uint8_t c = md.region_guess();
601 int hz;
602 int pal;
604 md::region_info(c, &pal, &hz, 0, 0, 0);
605 if ((hz != dgen_hz) || (pal != dgen_pal) || (c != md.region)) {
606 md.region = c;
607 dgen_hz = hz;
608 dgen_pal = pal;
609 printf("sdl: reconfiguring for region \"%c\": "
610 "%dHz (%s)\n", c, hz, (pal ? "PAL" : "NTSC"));
611 pd_graphics_reinit(dgen_sound, dgen_pal, dgen_hz);
612 if (dgen_sound) {
613 long rate = dgen_soundrate;
614 unsigned int samples;
616 pd_sound_deinit();
617 samples = (dgen_soundsegs * (rate / dgen_hz));
618 pd_sound_init(rate, samples);
620 md.pal = pal;
621 md.init_pal();
622 md.init_sound();
626 ram_load(md);
627 if (dgen_autoload) {
628 slot = 0;
629 md_load(md);
631 return (CMD_OK | CMD_MSG);
634 static void rehash_prompt_complete_common()
636 size_t i;
637 unsigned int common;
639 prompt.common = 0;
640 if ((prompt.complete == NULL) || (prompt.complete[0] == NULL))
641 return;
642 common = strlen(prompt.complete[0]);
643 for (i = 1; (prompt.complete[i] != NULL); ++i) {
644 unsigned int tmp;
646 tmp = strcommon(prompt.complete[i], prompt.complete[(i - 1)]);
647 if (tmp < common)
648 common = tmp;
649 if (common == 0)
650 break;
652 prompt.common = common;
655 static char* prompt_cmpl_load(class md& md, unsigned int ac, const char** av,
656 unsigned int len)
658 const char *prefix;
659 size_t i;
660 unsigned int skip;
662 (void)md;
663 assert(ac != 0);
664 if ((ac == 1) || (len == ~0u) || (av[(ac - 1)] == NULL)) {
665 prefix = "";
666 len = 0;
668 else
669 prefix = av[(ac - 1)];
670 if (prompt.complete == NULL) {
671 // Rebuild cache.
672 prompt.skip = 0;
673 prompt.complete = complete_path(prefix, len,
674 dgen_rom_path.val);
675 if (prompt.complete == NULL)
676 return NULL;
677 rehash_prompt_complete_common();
679 retry:
680 skip = prompt.skip;
681 for (i = 0; (prompt.complete[i] != NULL); ++i) {
682 if (skip == 0)
683 break;
684 --skip;
686 if (prompt.complete[i] == NULL) {
687 if (prompt.skip != 0) {
688 prompt.skip = 0;
689 goto retry;
691 return NULL;
693 ++prompt.skip;
694 return strdup(prompt.complete[i]);
697 static int prompt_cmd_unload(class md& md, unsigned int, const char**)
699 extern int slot;
700 extern void ram_save(class md&);
702 info.length = 0; // clear postponed messages
703 pd_message("No cartridge.");
704 ram_save(md);
705 if (dgen_autosave) {
706 slot = 0;
707 md_save(md);
709 if (md.unplug())
710 return (CMD_FAIL | CMD_MSG);
711 mdscr_splash();
712 return (CMD_OK | CMD_MSG);
715 static char* prompt_cmpl_config_file(class md& md, unsigned int ac,
716 const char** av, unsigned int len)
718 const char *prefix;
719 size_t i;
720 unsigned int skip;
722 (void)md;
723 assert(ac != 0);
724 if ((ac == 1) || (len == ~0u) || (av[(ac - 1)] == NULL)) {
725 prefix = "";
726 len = 0;
728 else
729 prefix = av[(ac - 1)];
730 if (prompt.complete == NULL) {
731 // Rebuild cache.
732 prompt.skip = 0;
733 prompt.complete = complete_path(prefix, len, "config");
734 if (prompt.complete == NULL)
735 return NULL;
736 rehash_prompt_complete_common();
738 retry:
739 skip = prompt.skip;
740 for (i = 0; (prompt.complete[i] != NULL); ++i) {
741 if (skip == 0)
742 break;
743 --skip;
745 if (prompt.complete[i] == NULL) {
746 if (prompt.skip != 0) {
747 prompt.skip = 0;
748 goto retry;
750 return NULL;
752 ++prompt.skip;
753 return strdup(prompt.complete[i]);
756 static int prompt_rehash_rc_field(const struct rc_field*, md&);
759 * Prompt "config_load" command handler.
760 * @param md Context.
761 * @param ac Number of arguments in av.
762 * @param av Arguments.
763 * @return Status code.
765 static int prompt_cmd_config_load(class md& md, unsigned int ac,
766 const char** av)
768 FILE *f;
769 char *s;
770 unsigned int i;
772 if (ac != 2)
773 return CMD_EINVAL;
774 s = backslashify((const uint8_t *)av[1], strlen(av[1]), 0, NULL);
775 if (s == NULL)
776 return CMD_FAIL;
777 f = dgen_fopen("config", av[1], (DGEN_READ | DGEN_CURRENT));
778 if (f == NULL) {
779 pd_message("Cannot load configuration \"%s\": %s.",
780 s, strerror(errno));
781 free(s);
782 return (CMD_FAIL | CMD_MSG);
784 parse_rc(f, av[1]);
785 fclose(f);
786 for (i = 0; (rc_fields[i].fieldname != NULL); ++i)
787 prompt_rehash_rc_field(&rc_fields[i], md);
788 pd_message("Loaded configuration \"%s\".", s);
789 free(s);
790 return (CMD_OK | CMD_MSG);
794 * Prompt "config_save" command handler.
795 * @param md Context.
796 * @param ac Number of arguments in av.
797 * @param av Arguments.
798 * @return Status code.
800 static int prompt_cmd_config_save(class md& md, unsigned int ac,
801 const char** av)
803 FILE *f;
804 char *s;
806 (void)md;
807 if (ac != 2)
808 return CMD_EINVAL;
809 s = backslashify((const uint8_t *)av[1], strlen(av[1]), 0, NULL);
810 if (s == NULL)
811 return CMD_FAIL;
812 f = dgen_fopen("config", av[1], (DGEN_WRITE | DGEN_TEXT));
813 if (f == NULL) {
814 pd_message("Cannot save configuration \"%s\": %s",
815 s, strerror(errno));
816 free(s);
817 return (CMD_FAIL | CMD_MSG);
819 dump_rc(f);
820 fclose(f);
821 pd_message("Saved configuration \"%s\"", s);
822 free(s);
823 return (CMD_OK | CMD_MSG);
826 static int prompt_cmd_reset(class md& md, unsigned int, const char**)
828 md.reset();
829 return CMD_OK;
832 static int prompt_cmd_unbind(class md&, unsigned int ac, const char** av)
834 unsigned int i;
835 int ret;
837 if (ac < 2)
838 return CMD_EINVAL;
839 ret = CMD_FAIL;
840 for (i = 1; (i != ac); ++i) {
841 struct rc_field *rcf = rc_fields;
843 while (rcf->fieldname != NULL) {
844 if ((rcf->parser == rc_bind) &&
845 (!strcasecmp(av[i], rcf->fieldname))) {
846 rc_binding_del(rcf);
847 ret = CMD_OK;
849 else
850 ++rcf;
853 return ret;
856 static char* prompt_cmpl_unbind(class md&, unsigned int ac,
857 const char** av, unsigned int len)
859 const struct rc_binding *rcb;
860 const char *prefix;
861 unsigned int skip;
863 assert(ac != 0);
864 if ((ac == 1) || (len == ~0u) || (av[(ac - 1)] == NULL)) {
865 prefix = "";
866 len = 0;
868 else
869 prefix = av[(ac - 1)];
870 skip = prompt.skip;
871 retry:
872 for (rcb = rc_binding_head.next;
873 (rcb != &rc_binding_head);
874 rcb = rcb->next) {
875 if (strncasecmp(prefix, rcb->rc, len))
876 continue;
877 if (skip == 0)
878 break;
879 --skip;
881 if (rcb == &rc_binding_head) {
882 if (prompt.skip != 0) {
883 prompt.skip = 0;
884 goto retry;
886 return NULL;
888 ++prompt.skip;
889 return strdup(rcb->rc);
892 #ifdef WITH_VGMDUMP
894 static char* prompt_cmpl_vgmdump(class md& md, unsigned int ac,
895 const char** av, unsigned int len)
897 const char *prefix;
898 size_t i;
899 unsigned int skip;
901 (void)md;
902 assert(ac != 0);
903 if ((ac == 1) || (len == ~0u) || (av[(ac - 1)] == NULL)) {
904 prefix = "";
905 len = 0;
907 else
908 prefix = av[(ac - 1)];
909 if (prompt.complete == NULL) {
910 // Rebuild cache.
911 prompt.skip = 0;
912 prompt.complete = complete_path(prefix, len, "vgm");
913 if (prompt.complete == NULL)
914 return NULL;
915 rehash_prompt_complete_common();
917 retry:
918 skip = prompt.skip;
919 for (i = 0; (prompt.complete[i] != NULL); ++i) {
920 if (skip == 0)
921 break;
922 --skip;
924 if (prompt.complete[i] == NULL) {
925 if (prompt.skip != 0) {
926 prompt.skip = 0;
927 goto retry;
929 return NULL;
931 ++prompt.skip;
932 return strdup(prompt.complete[i]);
935 static int prompt_cmd_vgmdump(class md& md, unsigned int ac, const char** av)
937 char *s;
939 if (ac < 2)
940 return CMD_EINVAL;
941 if (!strcasecmp(av[1], "stop")) {
942 if (md.vgm_dump == false)
943 pd_message("VGM dumping already stopped.");
944 else {
945 md.vgm_dump_stop();
946 pd_message("Stopped VGM dumping.");
948 return (CMD_OK | CMD_MSG);
950 if (strcasecmp(av[1], "start"))
951 return CMD_EINVAL;
952 if (ac < 3) {
953 pd_message("VGM file name required.");
954 return (CMD_EINVAL | CMD_MSG);
956 s = backslashify((const uint8_t *)av[2], strlen(av[2]), 0, NULL);
957 if (s == NULL)
958 return CMD_FAIL;
959 if (md.vgm_dump_start(av[2])) {
960 pd_message("Cannot dump VGM to \"%s\": %s",
961 s, strerror(errno));
962 free(s);
963 return (CMD_FAIL | CMD_MSG);
965 pd_message("Started VGM dumping to \"%s\"", s);
966 free(s);
967 return (CMD_OK | CMD_MSG);
970 #endif
972 struct filter_data {
973 bpp_t buf; ///< Input or output buffer.
974 unsigned int width; ///< Buffer width.
975 unsigned int height; ///< Buffer height.
976 unsigned int pitch; ///< Number of bytes per line in buffer.
977 void *data; ///< Filter-specific data.
978 bool updated:1; ///< Filter updated data to match its output.
979 bool failed:1; ///< Filter failed.
982 typedef void filter_func_t(const struct filter_data *in,
983 struct filter_data *out);
985 struct filter {
986 const char *name; ///< Filter name.
987 filter_func_t *func; ///< Filtering function.
988 bool safe:1; ///< Output buffer can be the same as input.
989 bool ctv:1; ///< Part of the CTV filters set.
990 bool resize:1; ///< Filter resizes input.
993 static filter_func_t filter_scale;
994 static filter_func_t filter_off;
995 static filter_func_t filter_stretch;
996 #ifdef WITH_SCALE2X
997 static filter_func_t filter_scale2x;
998 #endif
999 #ifdef WITH_HQX
1000 static filter_func_t filter_hqx;
1001 #endif
1002 #ifdef WITH_CTV
1003 static filter_func_t filter_blur;
1004 static filter_func_t filter_scanline;
1005 static filter_func_t filter_interlace;
1006 static filter_func_t filter_swab;
1008 static void set_swab();
1009 #endif
1011 static const struct filter filters_available[] = {
1012 { "stretch", filter_stretch, false, false, true },
1013 { "scale", filter_scale, false, false, true },
1014 #ifdef WITH_SCALE2X
1015 { "scale2x", filter_scale2x, false, false, true },
1016 #endif
1017 #ifdef WITH_HQX
1018 { "hqx", filter_hqx, false, false, true },
1019 #endif
1020 { "none", filter_off, true, false, true },
1021 #ifdef WITH_CTV
1022 // These filters must match ctv_names in rc.cpp.
1023 { "off", filter_off, true, true, false },
1024 { "blur", filter_blur, true, true, false },
1025 { "scanline", filter_scanline, true, true, false },
1026 { "interlace", filter_interlace, true, true, false },
1027 { "swab", filter_swab, true, true, false },
1028 #endif
1031 static unsigned int filters_stack_size;
1032 static bool filters_stack_default;
1033 static const struct filter *filters_stack[64];
1034 static bpp_t filters_stack_data_buf[2];
1035 static struct filter_data filters_stack_data[1 + elemof(filters_stack)];
1038 * Return filter structure associated with name.
1039 * @param name Name of filter.
1040 * @return Pointer to filter or NULL if not found.
1042 static const struct filter *filters_find(const char *name)
1044 size_t i;
1046 for (i = 0; (i != elemof(filters_available)); ++i)
1047 if (!strcasecmp(name, filters_available[i].name))
1048 return &filters_available[i];
1049 return NULL;
1053 * Update filters data, reallocate extra buffers if necessary.
1055 static void filters_stack_update()
1057 size_t i;
1058 const struct filter *f;
1059 struct filter_data *fd;
1060 unsigned int buffers;
1061 bpp_t buf[2];
1062 struct filter_data in_fd = {
1063 // Use the same formula as draw_scanline() in ras.cpp to avoid
1064 // the messy border for any supported depth.
1065 { ((uint8_t *)mdscr.data + (mdscr.pitch * 8) + 16) },
1066 video.width,
1067 video.height,
1068 (unsigned int)mdscr.pitch,
1069 NULL,
1070 false,
1071 false,
1073 struct filter_data out_fd = {
1074 { screen.buf.u8 },
1075 screen.width,
1076 (screen.height - screen.info_height),
1077 screen.pitch,
1078 NULL,
1079 false,
1080 false,
1082 struct filter_data *prev_fd;
1084 DEBUG(("updating filters data"));
1085 retry:
1086 assert(filters_stack_size <= elemof(filters_stack));
1087 buffers = 0;
1088 buf[0].u8 = filters_stack_data_buf[0].u8;
1089 buf[1].u8 = filters_stack_data_buf[1].u8;
1090 // Get the number of defined filters and count how many of them cannot
1091 // use the same buffer for both input and output.
1092 // Unless they are on top, "unsafe" filters require extra buffers.
1093 assert(filters_stack_data[0].data == NULL);
1094 for (i = 0; (i != elemof(filters_stack)); ++i) {
1095 if (i == filters_stack_size)
1096 break;
1097 f = filters_stack[i];
1098 assert(f != NULL);
1099 if ((f->safe == false) && (i != (filters_stack_size - 1)))
1100 ++buffers;
1101 // Clear filters stack output data.
1102 free(filters_stack_data[i + 1].data);
1104 memset(filters_stack_data, 0, sizeof(filters_stack_data));
1105 // Add a valid default filter if stack is empty.
1106 if (i == 0) {
1107 assert(filters_stack_size == 0);
1108 filters_stack[0] = &filters_available[0];
1109 ++filters_stack_size;
1110 filters_stack_default = true;
1111 goto retry;
1113 // Remove default filter if there is one and stack is not empty.
1114 else if ((i > 1) && (filters_stack_default == true)) {
1115 assert(filters_stack_size > 1);
1116 --filters_stack_size;
1117 memmove(&filters_stack[0], &filters_stack[1],
1118 (sizeof(filters_stack[0]) * filters_stack_size));
1119 filters_stack_default = false;
1120 goto retry;
1122 // Check if extra buffers are required.
1123 if (buffers) {
1124 if (buffers > 2)
1125 buffers = 2;
1126 else {
1127 // Remove unnecessary buffer.
1128 free(buf[1].u8);
1129 buf[1].u8 = NULL;
1130 filters_stack_data_buf[1].u8 = NULL;
1132 DEBUG(("requiring %u extra buffer(s)", buffers));
1133 // Reallocate them.
1134 for (i = 0; (i != buffers); ++i) {
1135 size_t size = (screen.pitch * screen.height);
1137 DEBUG(("temporary buffer %u size: %zu", i, size));
1138 buf[i].u8 =
1139 (uint8_t *)realloc((void *)buf[i].u8, size);
1140 if (size == 0) {
1141 assert(buf[i].u8 == NULL);
1142 DEBUG(("freed zero-sized buffer"));
1143 filters_stack_data_buf[i].u8 = NULL;
1144 continue;
1146 if (buf[i].u8 == NULL) {
1147 // Not good, remove one of the filters that
1148 // require an extra buffer and try again.
1149 free(filters_stack_data_buf[i].u8);
1150 filters_stack_data_buf[i].u8 = NULL;
1151 for (i = 0;
1152 (i < filters_stack_size);
1153 ++i) {
1154 if (filters_stack[i]->safe == true)
1155 continue;
1156 --filters_stack_size;
1157 memmove(&filters_stack[i],
1158 &filters_stack[i + 1],
1159 (sizeof(filters_stack[i]) *
1160 (filters_stack_size - i)));
1161 break;
1163 goto retry;
1165 filters_stack_data_buf[i].u8 = buf[i].u8;
1168 else {
1169 // No extra buffer required, deallocate them.
1170 DEBUG(("removing temporary buffers"));
1171 for (i = 0; (i != elemof(buf)); ++i) {
1172 free(buf[i].u8);
1173 buf[i].u8 = NULL;
1174 filters_stack_data_buf[i].u8 = NULL;
1177 // Update I/O buffers.
1178 buffers = 0;
1179 prev_fd = &filters_stack_data[0];
1180 memcpy(prev_fd, &in_fd, sizeof(*prev_fd));
1181 for (i = 0; (i != elemof(filters_stack)); ++i) {
1182 if (i == filters_stack_size)
1183 break;
1184 f = filters_stack[i];
1185 fd = &filters_stack_data[i + 1];
1186 // The last filter uses screen output.
1187 if (i == (filters_stack_size - 1))
1188 memcpy(fd, &out_fd, sizeof(*fd));
1189 // Safe filters have the same input as their output.
1190 else if (f->safe == true)
1191 memcpy(fd, prev_fd, sizeof(*fd));
1192 // Other filters output to a temporary buffer.
1193 else {
1194 fd->buf.u8 = buf[buffers].u8;
1195 fd->width = screen.width;
1196 fd->height = (screen.height - screen.info_height);
1197 fd->pitch = screen.pitch;
1198 fd->data = NULL;
1199 fd->updated = false;
1200 fd->failed = false;
1201 buffers ^= 1;
1203 prev_fd = fd;
1205 #ifndef NDEBUG
1206 DEBUG(("filters stack:"));
1207 for (i = 0; (i != filters_stack_size); ++i)
1208 DEBUG(("- %s (input: %p output: %p)",
1209 filters_stack[i]->name,
1210 (void *)filters_stack_data[i].buf.u8,
1211 (void *)filters_stack_data[i + 1].buf.u8));
1212 #endif
1213 screen_clear();
1217 * Add filter to stack.
1218 * @param f Filter to add.
1220 static void filters_push(const struct filter *f)
1222 assert(filters_stack_size <= elemof(filters_stack));
1223 if ((f == NULL) || (filters_stack_size == elemof(filters_stack)))
1224 return;
1225 DEBUG(("%s", f->name));
1226 filters_stack[filters_stack_size] = f;
1227 filters_stack_data[filters_stack_size + 1].data = NULL;
1228 ++filters_stack_size;
1229 filters_stack_update();
1233 * Insert filter at the bottom of the stack.
1234 * @param f Filter to insert.
1236 static void filters_insert(const struct filter *f)
1238 assert(filters_stack_size <= elemof(filters_stack));
1239 if ((f == NULL) ||
1240 (filters_stack_size == elemof(filters_stack)))
1241 return;
1242 DEBUG(("%s", f->name));
1243 memmove(&filters_stack[1], &filters_stack[0],
1244 (filters_stack_size * sizeof(filters_stack[0])));
1245 filters_stack[0] = f;
1246 filters_stack_data[0 + 1].data = NULL;
1247 ++filters_stack_size;
1248 filters_stack_update();
1251 // Currently unused.
1252 #if 0
1255 * Add filter to stack if not already in it.
1256 * @param f Filter to add.
1258 static void filters_push_once(const struct filter *f)
1260 size_t i;
1262 assert(filters_stack_size <= elemof(filters_stack));
1263 if (f == NULL)
1264 return;
1265 DEBUG(("%s", f->name));
1266 for (i = 0; (i != filters_stack_size); ++i)
1267 if (filters_stack[i] == f)
1268 return;
1269 filters_push(f);
1272 #endif
1274 #ifdef WITH_CTV
1277 * Remove last filter from stack.
1279 static void filters_pop()
1281 assert(filters_stack_size <= elemof(filters_stack));
1282 if (filters_stack_size) {
1283 --filters_stack_size;
1284 DEBUG(("%s", filters_stack[filters_stack_size]->name));
1285 free(filters_stack_data[filters_stack_size + 1].data);
1286 #ifndef NDEBUG
1287 memset(&filters_stack[filters_stack_size], 0xf0,
1288 sizeof(filters_stack[filters_stack_size]));
1289 memset(&filters_stack_data[filters_stack_size + 1], 0xf1,
1290 sizeof(filters_stack_data[filters_stack_size + 1]));
1291 #endif
1293 filters_stack_update();
1296 #endif
1299 * Remove a filter from anywhere in the stack.
1300 * @param index Filters stack index.
1302 static void filters_remove(unsigned int index)
1304 assert(filters_stack_size <= elemof(filters_stack));
1305 if (index >= filters_stack_size)
1306 return;
1307 --filters_stack_size;
1308 DEBUG(("%s", filters_stack[index]->name));
1309 free(filters_stack_data[index + 1].data);
1310 #ifndef NDEBUG
1311 memset(&filters_stack[index], 0xf2, sizeof(filters_stack[index]));
1312 memset(&filters_stack_data[index + 1], 0xf3,
1313 sizeof(filters_stack_data[index + 1]));
1314 #endif
1315 memmove(&filters_stack[index], &filters_stack[index + 1],
1316 (sizeof(filters_stack[index]) * (filters_stack_size - index)));
1317 memmove(&filters_stack_data[index + 1], &filters_stack_data[index + 2],
1318 (sizeof(filters_stack_data[index + 1]) *
1319 (filters_stack_size - index)));
1320 filters_stack_update();
1324 * Remove all occurences of filter from the stack.
1325 * @param f Filter to remove.
1327 static void filters_pluck(const struct filter *f)
1329 size_t i;
1331 assert(filters_stack_size <= elemof(filters_stack));
1332 if (f == NULL)
1333 return;
1334 DEBUG(("%s", f->name));
1335 for (i = 0; (i < filters_stack_size); ++i) {
1336 if (filters_stack[i] != f)
1337 continue;
1338 --filters_stack_size;
1339 DEBUG(("%s", filters_stack[i]->name));
1340 free(filters_stack_data[i + 1].data);
1341 #ifndef NDEBUG
1342 memset(&filters_stack[i], 0xf4, sizeof(filters_stack[i]));
1343 memset(&filters_stack_data[i + 1], 0xf5,
1344 sizeof(filters_stack_data[i + 1]));
1345 #endif
1346 memmove(&filters_stack[i], &filters_stack[i + 1],
1347 (sizeof(filters_stack[i]) * (filters_stack_size - i)));
1348 memmove(&filters_stack_data[i + 1], &filters_stack_data[i + 2],
1349 (sizeof(filters_stack_data[i + 1]) *
1350 (filters_stack_size - i)));
1351 --i;
1353 filters_stack_update();
1356 #ifdef WITH_CTV
1359 * Remove all occurences of CTV filters from the stack.
1361 static void filters_pluck_ctv()
1363 size_t i;
1365 assert(filters_stack_size <= elemof(filters_stack));
1366 for (i = 0; (i < filters_stack_size); ++i) {
1367 if (filters_stack[i]->ctv == false)
1368 continue;
1369 --filters_stack_size;
1370 DEBUG(("%s", filters_stack[i]->name));
1371 free(filters_stack_data[i + 1].data);
1372 #ifndef NDEBUG
1373 memset(&filters_stack[i], 0xf6, sizeof(filters_stack[i]));
1374 memset(&filters_stack_data[i + 1], 0xf6,
1375 sizeof(filters_stack_data[i + 1]));
1376 #endif
1377 memmove(&filters_stack[i], &filters_stack[i + 1],
1378 (sizeof(filters_stack[i]) * (filters_stack_size - i)));
1379 memmove(&filters_stack_data[i + 1], &filters_stack_data[i + 2],
1380 (sizeof(filters_stack_data[i + 1]) *
1381 (filters_stack_size - i)));
1382 --i;
1384 filters_stack_update();
1387 #endif
1389 #ifdef WITH_CTV
1392 * Empty filters stack.
1394 static void filters_empty()
1396 size_t i;
1398 assert(filters_stack_size <= elemof(filters_stack));
1399 DEBUG(("stack size was %u", filters_stack_size));
1400 for (i = 0; (i < filters_stack_size); ++i)
1401 free(filters_stack_data[i + 1].data);
1402 filters_stack_size = 0;
1403 #ifndef NDEBUG
1404 memset(filters_stack, 0xb0, sizeof(filters_stack));
1405 memset(filters_stack_data, 0xb0, sizeof(filters_stack_data));
1406 filters_stack_data[0].data = NULL;
1407 #endif
1408 filters_stack_update();
1411 #endif
1414 * Take a screenshot.
1416 static void do_screenshot(md& megad)
1418 static unsigned int n = 0;
1419 static char romname_old[sizeof(megad.romname)];
1420 FILE *fp;
1421 #ifdef HAVE_FTELLO
1422 off_t pos;
1423 #else
1424 long pos;
1425 #endif
1426 bpp_t line;
1427 unsigned int width;
1428 unsigned int height;
1429 unsigned int pitch;
1430 unsigned int bpp = mdscr.bpp;
1431 uint8_t (*out)[3]; // 24 bpp
1432 char name[(sizeof(megad.romname) + 32)];
1434 if (dgen_raw_screenshots) {
1435 width = video.width;
1436 height = video.height;
1437 pitch = mdscr.pitch;
1438 line.u8 = ((uint8_t *)mdscr.data + (pitch * 8) + 16);
1440 else {
1441 width = screen.width;
1442 height = screen.height;
1443 pitch = screen.pitch;
1444 line = screen.buf;
1446 switch (bpp) {
1447 case 15:
1448 case 16:
1449 case 24:
1450 case 32:
1451 break;
1452 default:
1453 pd_message("Screenshots unsupported in %d bpp.", bpp);
1454 return;
1456 // Make take a long time, let the main loop know about it.
1457 stopped = 1;
1458 // If megad.romname is different from last time, reset n.
1459 if (memcmp(romname_old, megad.romname, sizeof(romname_old))) {
1460 memcpy(romname_old, megad.romname, sizeof(romname_old));
1461 n = 0;
1463 retry:
1464 snprintf(name, sizeof(name), "%s-%06u.tga",
1465 ((megad.romname[0] == '\0') ? "unknown" : megad.romname), n);
1466 fp = dgen_fopen("screenshots", name, DGEN_APPEND);
1467 if (fp == NULL) {
1468 pd_message("Can't open %s.", name);
1469 return;
1471 fseek(fp, 0, SEEK_END);
1472 #ifdef HAVE_FTELLO
1473 pos = ftello(fp);
1474 #else
1475 pos = ftell(fp);
1476 #endif
1477 if (((off_t)pos == (off_t)-1) || ((off_t)pos != (off_t)0)) {
1478 fclose(fp);
1479 n = ((n + 1) % 1000000);
1480 goto retry;
1482 // Allocate line buffer.
1483 if ((out = (uint8_t (*)[3])malloc(sizeof(*out) * width)) == NULL)
1484 goto error;
1485 // Header
1487 uint8_t tmp[(3 + 5)] = {
1488 0x00, // length of the image ID field
1489 0x00, // whether a color map is included
1490 0x02 // image type: uncompressed, true-color image
1491 // 5 bytes of color map specification
1494 if (!fwrite(tmp, sizeof(tmp), 1, fp))
1495 goto error;
1498 uint16_t tmp[4] = {
1499 0, // x-origin
1500 0, // y-origin
1501 h2le16(width), // width
1502 h2le16(height) // height
1505 if (!fwrite(tmp, sizeof(tmp), 1, fp))
1506 goto error;
1509 uint8_t tmp[2] = {
1510 24, // always output 24 bits per pixel
1511 (1 << 5) // top-left origin
1514 if (!fwrite(tmp, sizeof(tmp), 1, fp))
1515 goto error;
1517 // Data
1518 switch (bpp) {
1519 unsigned int y;
1520 unsigned int x;
1522 case 15:
1523 for (y = 0; (y < height); ++y) {
1524 if (screen_lock())
1525 goto error;
1526 for (x = 0; (x < width); ++x) {
1527 uint16_t v = line.u16[x];
1529 out[x][0] = ((v << 3) & 0xf8);
1530 out[x][1] = ((v >> 2) & 0xf8);
1531 out[x][2] = ((v >> 7) & 0xf8);
1533 screen_unlock();
1534 if (!fwrite(out, (sizeof(*out) * width), 1, fp))
1535 goto error;
1536 line.u8 += pitch;
1538 break;
1539 case 16:
1540 for (y = 0; (y < height); ++y) {
1541 if (screen_lock())
1542 goto error;
1543 for (x = 0; (x < width); ++x) {
1544 uint16_t v = line.u16[x];
1546 out[x][0] = ((v << 3) & 0xf8);
1547 out[x][1] = ((v >> 3) & 0xfc);
1548 out[x][2] = ((v >> 8) & 0xf8);
1550 screen_unlock();
1551 if (!fwrite(out, (sizeof(*out) * width), 1, fp))
1552 goto error;
1553 line.u8 += pitch;
1555 break;
1556 case 24:
1557 for (y = 0; (y < height); ++y) {
1558 if (screen_lock())
1559 goto error;
1560 #ifdef WORDS_BIGENDIAN
1561 for (x = 0; (x < width); ++x) {
1562 out[x][0] = line.u24[x][2];
1563 out[x][1] = line.u24[x][1];
1564 out[x][2] = line.u24[x][0];
1566 #else
1567 memcpy(out, line.u24, (sizeof(*out) * width));
1568 #endif
1569 screen_unlock();
1570 if (!fwrite(out, (sizeof(*out) * width), 1, fp))
1571 goto error;
1572 line.u8 += pitch;
1574 break;
1575 case 32:
1576 for (y = 0; (y < height); ++y) {
1577 if (screen_lock())
1578 goto error;
1579 for (x = 0; (x < width); ++x) {
1580 #ifdef WORDS_BIGENDIAN
1581 uint32_t rgb = h2le32(line.u32[x]);
1583 memcpy(&(out[x]), &rgb, 3);
1584 #else
1585 memcpy(&(out[x]), &(line.u32[x]), 3);
1586 #endif
1588 screen_unlock();
1589 if (!fwrite(out, (sizeof(*out) * width), 1, fp))
1590 goto error;
1591 line.u8 += pitch;
1593 break;
1595 pd_message("Screenshot written to %s.", name);
1596 free(out);
1597 fclose(fp);
1598 return;
1599 error:
1600 pd_message("Error while generating screenshot %s.", name);
1601 free(out);
1602 fclose(fp);
1606 * SDL flags help.
1608 void pd_help()
1610 printf(
1611 #ifdef WITH_OPENGL
1612 " -g (1|0) Enable/disable OpenGL.\n"
1613 #endif
1614 " -f Attempt to run fullscreen.\n"
1615 " -X scale Scale the screen in the X direction.\n"
1616 " -Y scale Scale the screen in the Y direction.\n"
1617 " -S scale Scale the screen by the same amount in both directions.\n"
1618 " -G WxH Desired window size.\n"
1623 * Handle rc variables
1625 void pd_rc()
1627 // Set stuff up from the rcfile first, so we can override it with
1628 // command-line options
1629 if (dgen_scale >= 1) {
1630 dgen_x_scale = dgen_scale;
1631 dgen_y_scale = dgen_scale;
1633 #ifdef WITH_CTV
1634 set_swab();
1635 #endif
1639 * Handle the switches.
1640 * @param c Switch's value.
1642 void pd_option(char c, const char *)
1644 int xs, ys;
1646 switch (c) {
1647 #ifdef WITH_OPENGL
1648 case 'g':
1649 dgen_opengl = atoi(optarg);
1650 break;
1651 #endif
1652 case 'f':
1653 dgen_fullscreen = 1;
1654 break;
1655 case 'X':
1656 if ((xs = atoi(optarg)) <= 0)
1657 break;
1658 dgen_x_scale = xs;
1659 break;
1660 case 'Y':
1661 if ((ys = atoi(optarg)) <= 0)
1662 break;
1663 dgen_y_scale = ys;
1664 break;
1665 case 'S':
1666 if ((xs = atoi(optarg)) <= 0)
1667 break;
1668 dgen_x_scale = xs;
1669 dgen_y_scale = xs;
1670 break;
1671 case 'G':
1672 if ((sscanf(optarg, " %d x %d ", &xs, &ys) != 2) ||
1673 (xs < 0) || (ys < 0))
1674 break;
1675 dgen_width = xs;
1676 dgen_height = ys;
1677 break;
1681 #ifdef WITH_OPENGL
1683 #ifdef WORDS_BIGENDIAN
1684 #define TEXTURE_16_TYPE GL_UNSIGNED_SHORT_5_6_5
1685 #define TEXTURE_32_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
1686 #else
1687 #define TEXTURE_16_TYPE GL_UNSIGNED_SHORT_5_6_5
1688 #define TEXTURE_32_TYPE GL_UNSIGNED_BYTE
1689 #endif
1691 static void texture_init_id(struct texture& texture)
1693 GLint param;
1695 if (texture.linear)
1696 param = GL_LINEAR;
1697 else
1698 param = GL_NEAREST;
1699 glBindTexture(GL_TEXTURE_2D, texture.id);
1700 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1701 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1702 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
1703 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param);
1704 if (texture.u32 == 0)
1705 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
1706 texture.width, texture.height,
1707 0, GL_RGB, TEXTURE_16_TYPE,
1708 texture.buf.u16);
1709 else
1710 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
1711 texture.width, texture.height,
1712 0, GL_BGRA, TEXTURE_32_TYPE,
1713 texture.buf.u32);
1716 static void texture_init_dlist(struct texture& texture)
1718 glNewList(texture.dlist, GL_COMPILE);
1719 glMatrixMode(GL_MODELVIEW);
1720 glPushMatrix();
1721 glLoadIdentity();
1722 glOrtho(0, texture.vis_width, texture.vis_height, 0, 0, 1);
1723 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1724 glEnable(GL_TEXTURE_2D);
1725 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
1727 glBindTexture(GL_TEXTURE_2D, texture.id);
1728 glBegin(GL_QUADS);
1729 glTexCoord2i(0, 1);
1730 glVertex2i(0, texture.height); // lower left
1731 glTexCoord2i(0, 0);
1732 glVertex2i(0, 0); // upper left
1733 glTexCoord2i(1, 0);
1734 glVertex2i(texture.width, 0); // upper right
1735 glTexCoord2i(1, 1);
1736 glVertex2i(texture.width, texture.height); // lower right
1737 glEnd();
1739 glDisable(GL_TEXTURE_2D);
1740 glPopMatrix();
1741 glEndList();
1745 * Round a value up to nearest power of two.
1746 * @param v Value.
1747 * @return Rounded value.
1749 static uint32_t roundup2(uint32_t v)
1751 --v;
1752 v |= (v >> 1);
1753 v |= (v >> 2);
1754 v |= (v >> 4);
1755 v |= (v >> 8);
1756 v |= (v >> 16);
1757 ++v;
1758 return v;
1761 static void release_texture(struct texture& texture)
1763 if ((texture.dlist != 0) && (glIsList(texture.dlist))) {
1764 glDeleteTextures(1, &texture.id);
1765 glDeleteLists(texture.dlist, 1);
1766 texture.dlist = 0;
1768 free(texture.buf.u32);
1769 texture.buf.u32 = NULL;
1772 static int init_texture(struct screen *screen)
1774 struct texture& texture = screen->texture;
1775 unsigned int vis_width;
1776 unsigned int vis_height;
1777 unsigned int x;
1778 unsigned int y;
1779 unsigned int w;
1780 unsigned int h;
1781 void *tmp;
1782 size_t i;
1783 GLenum error;
1785 // When bool_opengl_stretch is enabled, width and height are redefined
1786 // using X and Y scale factors with additional room for the info bar.
1787 if (dgen_opengl_stretch) {
1788 vis_width = (video.width *
1789 (screen->x_scale ? screen->x_scale : 1));
1790 vis_height = (video.height *
1791 (screen->y_scale ? screen->y_scale : 1));
1792 vis_height += screen->info_height;
1793 if (dgen_aspect) {
1794 // Keep scaled aspect ratio.
1795 w = ((screen->height * vis_width) / vis_height);
1796 h = ((screen->width * vis_height) / vis_width);
1797 if (w >= screen->width) {
1798 w = screen->width;
1799 if (h == 0)
1800 ++h;
1802 else {
1803 h = screen->height;
1804 if (w == 0)
1805 ++w;
1808 else {
1809 // Aspect ratio won't be kept.
1810 w = screen->width;
1811 h = screen->height;
1814 else {
1815 w = vis_width = screen->width;
1816 h = vis_height = screen->height;
1818 if (screen->width > w)
1819 x = ((screen->width - w) / 2);
1820 else
1821 x = 0;
1822 if (screen->height > h)
1823 y = ((screen->height - h) / 2);
1824 else
1825 y = 0;
1826 DEBUG(("initializing for width=%u height=%u", vis_width, vis_height));
1827 // Set viewport.
1828 DEBUG(("glViewport(%u, %u, %u, %u)", x, y, w, h));
1829 glViewport(x, y, w, h);
1830 // Disable dithering
1831 glDisable(GL_DITHER);
1832 // Disable anti-aliasing
1833 glDisable(GL_LINE_SMOOTH);
1834 glDisable(GL_POINT_SMOOTH);
1835 // Disable depth buffer
1836 glDisable(GL_DEPTH_TEST);
1838 glClearColor(0.0, 0.0, 0.0, 0.0);
1839 glShadeModel(GL_FLAT);
1841 // Initialize and allocate texture.
1842 texture.u32 = (!!dgen_opengl_32bit);
1843 texture.linear = (!!dgen_opengl_linear);
1844 texture.width = roundup2(vis_width);
1845 texture.height = roundup2(vis_height);
1846 if (dgen_opengl_square) {
1847 // Texture must be square.
1848 if (texture.width < texture.height)
1849 texture.width = texture.height;
1850 else
1851 texture.height = texture.width;
1853 texture.vis_width = vis_width;
1854 texture.vis_height = vis_height;
1855 DEBUG(("texture width=%u height=%u", texture.width, texture.height));
1856 if ((texture.width == 0) || (texture.height == 0))
1857 goto fail;
1858 i = ((texture.width * texture.height) * (2 << texture.u32));
1859 DEBUG(("texture size=%lu (%u Bpp)",
1860 (unsigned long)i, (2 << texture.u32)));
1861 if ((tmp = realloc(texture.buf.u32, i)) == NULL)
1862 goto fail;
1863 memset(tmp, 0, i);
1864 texture.buf.u32 = (uint32_t *)tmp;
1865 if ((texture.dlist != 0) && (glIsList(texture.dlist))) {
1866 glDeleteTextures(1, &texture.id);
1867 glDeleteLists(texture.dlist, 1);
1869 DEBUG(("texture buf=%p", (void *)texture.buf.u32));
1870 if ((texture.dlist = glGenLists(1)) == 0)
1871 goto fail;
1872 if ((glGenTextures(1, &texture.id), error = glGetError()) ||
1873 (texture_init_id(texture), error = glGetError()) ||
1874 (texture_init_dlist(texture), error = glGetError())) {
1875 // Do something with "error".
1876 goto fail;
1878 DEBUG(("texture initialization OK"));
1879 return 0;
1880 fail:
1881 release_texture(texture);
1882 DEBUG(("texture initialization failed"));
1883 return -1;
1886 static void update_texture(struct texture& texture)
1888 glBindTexture(GL_TEXTURE_2D, texture.id);
1889 if (texture.u32 == 0)
1890 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
1891 texture.vis_width, texture.vis_height,
1892 GL_RGB, TEXTURE_16_TYPE,
1893 texture.buf.u16);
1894 else
1895 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
1896 texture.vis_width, texture.vis_height,
1897 GL_BGRA, TEXTURE_32_TYPE,
1898 texture.buf.u32);
1899 glCallList(texture.dlist);
1900 SDL_GL_SwapBuffers();
1903 #endif // WITH_OPENGL
1906 * This filter passes input to output unchanged, only centered or truncated
1907 * if necessary. Doesn't have any fallback, thus cannot fail.
1908 * @param in Input buffer data.
1909 * @param out Output buffer data.
1911 static void filter_off(const struct filter_data *in, struct filter_data *out)
1913 unsigned int line;
1914 unsigned int height;
1915 uint8_t *in_buf;
1916 uint8_t *out_buf;
1918 // Check if copying is necessary.
1919 if (in->buf.u8 == out->buf.u8)
1920 return;
1921 // Copy line by line and center.
1922 if (in->height > out->height)
1923 height = out->height;
1924 else
1925 height = in->height;
1926 if (out->updated == false) {
1927 if (in->width <= out->width) {
1928 unsigned int x_off = ((out->width - in->width) / 2);
1929 unsigned int y_off = ((out->height - height) / 2);
1931 out->buf.u8 += (x_off * screen.Bpp);
1932 out->buf.u8 += (out->pitch * y_off);
1933 out->width = in->width;
1935 out->height = height;
1936 out->updated = true;
1938 in_buf = in->buf.u8;
1939 out_buf = out->buf.u8;
1940 for (line = 0; (line != height); ++line) {
1941 memcpy(out_buf, in_buf, (out->width * screen.Bpp));
1942 in_buf += in->pitch;
1943 out_buf += out->pitch;
1947 // Copy/rescale functions.
1949 struct filter_scale_data {
1950 unsigned int x_scale;
1951 unsigned int y_scale;
1952 filter_func_t *filter;
1955 template <typename uintX_t>
1956 static void filter_scale_X(const struct filter_data *in,
1957 struct filter_data *out)
1959 struct filter_scale_data *data =
1960 (struct filter_scale_data *)out->data;
1961 uintX_t *dst = (uintX_t *)out->buf.u32;
1962 unsigned int dst_pitch = out->pitch;
1963 uintX_t *src = (uintX_t *)in->buf.u32;
1964 unsigned int src_pitch = in->pitch;
1965 unsigned int width = in->width;
1966 unsigned int x_scale = data->x_scale;
1967 unsigned int y_scale = data->y_scale;
1968 unsigned int height = in->height;
1969 unsigned int y;
1971 for (y = 0; (y != height); ++y) {
1972 uintX_t *out = dst;
1973 unsigned int i;
1974 unsigned int x;
1976 for (x = 0; (x != width); ++x) {
1977 uintX_t tmp = src[x];
1979 for (i = 0; (i != x_scale); ++i)
1980 *(out++) = tmp;
1982 out = dst;
1983 dst = (uintX_t *)((uint8_t *)dst + dst_pitch);
1984 for (i = 1; (i < y_scale); ++i) {
1985 memcpy(dst, out, (width * sizeof(*dst) * x_scale));
1986 out = dst;
1987 dst = (uintX_t *)((uint8_t *)dst + dst_pitch);
1989 src = (uintX_t *)((uint8_t *)src + src_pitch);
1993 static void filter_scale_3(const struct filter_data *in,
1994 struct filter_data *out)
1996 struct filter_scale_data *data =
1997 (struct filter_scale_data *)out->data;
1998 uint24_t *dst = out->buf.u24;
1999 unsigned int dst_pitch = out->pitch;
2000 uint24_t *src = in->buf.u24;
2001 unsigned int src_pitch = in->pitch;
2002 unsigned int width = in->width;
2003 unsigned int x_scale = data->x_scale;
2004 unsigned int y_scale = data->y_scale;
2005 unsigned int height = in->height;
2006 unsigned int y;
2008 for (y = 0; (y != height); ++y) {
2009 uint24_t *out = dst;
2010 unsigned int i;
2011 unsigned int x;
2013 for (x = 0; (x != width); ++x) {
2014 uint24_t tmp;
2016 u24cpy(&tmp, &src[x]);
2017 for (i = 0; (i != x_scale); ++i)
2018 u24cpy((out++), &tmp);
2020 out = dst;
2021 dst = (uint24_t *)((uint8_t *)dst + dst_pitch);
2022 for (i = 1; (i < y_scale); ++i) {
2023 memcpy(dst, out, (width * sizeof(*dst) * x_scale));
2024 out = dst;
2025 dst = (uint24_t *)((uint8_t *)dst + dst_pitch);
2027 src = (uint24_t *)((uint8_t *)src + src_pitch);
2032 * This filter attempts to rescale according to screen X/Y factors.
2033 * @param in Input buffer data.
2034 * @param out Output buffer data.
2036 static void filter_scale(const struct filter_data *in,
2037 struct filter_data *out)
2039 static const struct {
2040 unsigned int Bpp;
2041 filter_func_t *func;
2042 } scale_mode[] = {
2043 { 1, filter_scale_X<uint8_t> },
2044 { 2, filter_scale_X<uint16_t> },
2045 { 3, filter_scale_3 },
2046 { 4, filter_scale_X<uint32_t> },
2048 struct filter_scale_data *data;
2049 unsigned int width;
2050 unsigned int height;
2051 unsigned int x_off;
2052 unsigned int y_off;
2053 unsigned int x_scale;
2054 unsigned int y_scale;
2055 filter_func_t *filter;
2056 unsigned int i;
2058 if (out->failed == true) {
2059 failed:
2060 filter_off(in, out);
2061 return;
2063 if (out->updated == true) {
2064 data = (struct filter_scale_data *)out->data;
2065 filter = data->filter;
2066 process:
2067 // Feed this to the basic scaler.
2068 (*filter)(in, out);
2069 return;
2071 // Initialize filter.
2072 assert(out->data == NULL);
2073 x_scale = screen.x_scale;
2074 y_scale = screen.y_scale;
2075 while ((width = (in->width * x_scale)) > out->width)
2076 --x_scale;
2077 while ((height = (in->height * y_scale)) > out->height)
2078 --y_scale;
2079 // Check whether output is large enough.
2080 if ((x_scale == 0) || (y_scale == 0)) {
2081 DEBUG(("cannot rescale by %ux%u", x_scale, y_scale));
2082 out->failed = true;
2083 goto failed;
2085 // Not rescaling is faster through filter_off().
2086 if ((x_scale == 1) && (y_scale == 1)) {
2087 DEBUG(("using faster fallback for %ux%u", x_scale, y_scale));
2088 out->failed = true;
2089 goto failed;
2091 // Find a suitable filter.
2092 for (i = 0; (i != elemof(scale_mode)); ++i)
2093 if (scale_mode[i].Bpp == screen.Bpp)
2094 break;
2095 if (i == elemof(scale_mode)) {
2096 DEBUG(("%u Bpp depth is not supported", screen.Bpp));
2097 out->failed = true;
2098 goto failed;
2100 DEBUG(("using %u Bpp function to scale by %ux%u",
2101 screen.Bpp, x_scale, y_scale));
2102 data = (struct filter_scale_data *)malloc(sizeof(*data));
2103 if (data == NULL) {
2104 DEBUG(("allocation failure"));
2105 out->failed = true;
2106 goto failed;
2108 filter = scale_mode[i].func;
2109 data->filter = filter;
2110 data->x_scale = x_scale;
2111 data->y_scale = y_scale;
2112 // Center output.
2113 x_off = ((out->width - width) / 2);
2114 y_off = ((out->height - height) / 2);
2115 out->buf.u8 += (x_off * screen.Bpp);
2116 out->buf.u8 += (out->pitch * y_off);
2117 out->width = width;
2118 out->height = height;
2119 out->data = (void *)data;
2120 out->updated = true;
2121 goto process;
2124 struct filter_stretch_data {
2125 uint8_t *h_table;
2126 uint8_t *v_table;
2127 filter_func_t *filter;
2130 template <typename uintX_t>
2131 static void filter_stretch_X(const struct filter_data *in,
2132 struct filter_data *out)
2134 struct filter_stretch_data *data =
2135 (struct filter_stretch_data *)out->data;
2136 uint8_t *h_table = data->h_table;
2137 uint8_t *v_table = data->v_table;
2138 uintX_t *dst = (uintX_t *)out->buf.u8;
2139 unsigned int dst_pitch = out->pitch;
2140 unsigned int dst_w = out->width;
2141 uintX_t *src = (uintX_t *)in->buf.u8;
2142 unsigned int src_pitch = in->pitch;
2143 unsigned int src_w = in->width;
2144 unsigned int src_h = in->height;
2145 unsigned int src_y;
2147 dst_pitch /= sizeof(*dst);
2148 src_pitch /= sizeof(*src);
2149 for (src_y = 0; (src_y != src_h); ++src_y) {
2150 uint8_t v_repeat = v_table[src_y];
2151 unsigned int src_x;
2152 unsigned int dst_x;
2154 if (!v_repeat) {
2155 src += src_pitch;
2156 continue;
2158 for (src_x = 0, dst_x = 0; (src_x != src_w); ++src_x) {
2159 uint8_t h_repeat = h_table[src_x];
2161 if (!h_repeat)
2162 continue;
2163 while (h_repeat--)
2164 dst[dst_x++] = src[src_x];
2166 dst += dst_pitch;
2167 while (--v_repeat) {
2168 memcpy(dst, (dst - dst_pitch), (dst_w * sizeof(*dst)));
2169 dst += dst_pitch;
2171 src += src_pitch;
2175 static void filter_stretch_3(const struct filter_data *in,
2176 struct filter_data *out)
2178 struct filter_stretch_data *data =
2179 (struct filter_stretch_data *)out->data;
2180 uint8_t *h_table = data->h_table;
2181 uint8_t *v_table = data->v_table;
2182 uint24_t *dst = out->buf.u24;
2183 unsigned int dst_pitch = out->pitch;
2184 unsigned int dst_w = out->width;
2185 uint24_t *src = in->buf.u24;
2186 unsigned int src_pitch = in->pitch;
2187 unsigned int src_w = in->width;
2188 unsigned int src_h = in->height;
2189 unsigned int src_y;
2191 dst_pitch /= sizeof(*dst);
2192 src_pitch /= sizeof(*src);
2193 for (src_y = 0; (src_y != src_h); ++src_y) {
2194 uint8_t v_repeat = v_table[src_y];
2195 unsigned int src_x;
2196 unsigned int dst_x;
2198 if (!v_repeat) {
2199 src += src_pitch;
2200 continue;
2202 for (src_x = 0, dst_x = 0; (src_x != src_w); ++src_x) {
2203 uint8_t h_repeat = h_table[src_x];
2205 if (!h_repeat)
2206 continue;
2207 while (h_repeat--)
2208 u24cpy(&dst[dst_x++], &src[src_x]);
2210 dst += dst_pitch;
2211 while (--v_repeat) {
2212 memcpy(dst, (dst - dst_pitch), (dst_w * sizeof(*dst)));
2213 dst += dst_pitch;
2215 src += src_pitch;
2220 * This filter stretches the input buffer to fill the entire output.
2221 * @param in Input buffer data.
2222 * @param out Output buffer data.
2224 static void filter_stretch(const struct filter_data *in,
2225 struct filter_data *out)
2227 static const struct {
2228 unsigned int Bpp;
2229 filter_func_t *func;
2230 } stretch_mode[] = {
2231 { 1, filter_stretch_X<uint8_t> },
2232 { 2, filter_stretch_X<uint16_t> },
2233 { 3, filter_stretch_3 },
2234 { 4, filter_stretch_X<uint32_t> },
2236 struct filter_stretch_data *data;
2237 unsigned int dst_w;
2238 unsigned int dst_h;
2239 unsigned int src_w;
2240 unsigned int src_h;
2241 unsigned int h_ratio;
2242 unsigned int v_ratio;
2243 unsigned int dst_x;
2244 unsigned int dst_y;
2245 unsigned int src_x;
2246 unsigned int src_y;
2247 filter_func_t *filter;
2248 unsigned int i;
2250 if (out->failed == true) {
2251 failed:
2252 filter_off(in, out);
2253 return;
2255 if (out->updated == true) {
2256 data = (struct filter_stretch_data *)out->data;
2257 filter = data->filter;
2258 process:
2259 (*filter)(in, out);
2260 return;
2262 // Initialize filter.
2263 assert(out->data == NULL);
2264 dst_w = out->width;
2265 dst_h = out->height;
2266 src_w = in->width;
2267 src_h = in->height;
2268 if ((src_h == 0) || (src_w == 0)) {
2269 DEBUG(("invalid input size: %ux%u", src_h, src_w));
2270 out->failed = true;
2271 goto failed;
2273 // Make sure input and output pitches are multiples of pixel size
2274 // at the current depth.
2275 if ((in->pitch % screen.Bpp) || (out->pitch % screen.Bpp)) {
2276 DEBUG(("Bpp: %u, in->pitch: %u, out->pitch: %u",
2277 screen.Bpp, in->pitch, out->pitch));
2278 out->failed = true;
2279 goto failed;
2281 // Find a suitable filter.
2282 for (i = 0; (i != elemof(stretch_mode)); ++i)
2283 if (stretch_mode[i].Bpp == screen.Bpp)
2284 break;
2285 if (i == elemof(stretch_mode)) {
2286 DEBUG(("%u Bpp depth is not supported", screen.Bpp));
2287 out->failed = true;
2288 goto failed;
2290 filter = stretch_mode[i].func;
2291 // Fix output if original aspect ratio must be kept.
2292 if (dgen_aspect) {
2293 unsigned int w = ((dst_h * src_w) / src_h);
2294 unsigned int h = ((dst_w * src_h) / src_w);
2296 if (w >= dst_w) {
2297 w = dst_w;
2298 if (h == 0)
2299 ++h;
2301 else {
2302 h = dst_h;
2303 if (w == 0)
2304 ++w;
2306 dst_w = w;
2307 dst_h = h;
2309 // Precompute H and V pixel ratios.
2310 h_ratio = ((dst_w << 10) / src_w);
2311 v_ratio = ((dst_h << 10) / src_h);
2312 data = (struct filter_stretch_data *)
2313 calloc(1, sizeof(*data) + src_w + src_h);
2314 if (data == NULL) {
2315 DEBUG(("allocation failure"));
2316 out->failed = true;
2317 goto failed;
2319 DEBUG(("stretching %ux%u to %ux%u/%ux%u (aspect ratio %s)",
2320 src_w, src_h, dst_w, dst_h, out->width, out->height,
2321 (dgen_aspect ? "must be kept" : "is free")));
2322 data->h_table = (uint8_t *)(data + 1);
2323 data->v_table = (data->h_table + src_w);
2324 data->filter = filter;
2325 for (dst_x = 0; (dst_x != dst_w); ++dst_x) {
2326 src_x = ((dst_x << 10) / h_ratio);
2327 if (src_x < src_w)
2328 ++data->h_table[src_x];
2330 for (dst_y = 0; (dst_y != dst_h); ++dst_y) {
2331 src_y = ((dst_y << 10) / v_ratio);
2332 if (src_y < src_h)
2333 ++data->v_table[src_y];
2335 // Center output.
2336 dst_x = ((out->width - dst_w) / 2);
2337 dst_y = ((out->height - dst_h) / 2);
2338 out->buf.u8 += (dst_x * screen.Bpp);
2339 out->buf.u8 += (out->pitch * dst_y);
2340 out->width = dst_w;
2341 out->height = dst_h;
2342 out->data = (void *)data;
2343 out->updated = true;
2344 goto process;
2347 #ifdef WITH_HQX
2350 * This filter attempts to rescale with HQX.
2351 * @param in Input buffer data.
2352 * @param out Output buffer data.
2354 static void filter_hqx(const struct filter_data *in, struct filter_data *out)
2356 typedef void hqx_func_t(void *src, uint32_t src_pitch,
2357 void *dst, uint32_t dst_pitch,
2358 int width, int height);
2360 static const struct {
2361 unsigned int Bpp;
2362 unsigned int scale;
2363 hqx_func_t *func;
2364 } hqx_mode[] = {
2365 { 2, 2, (hqx_func_t *)hq2x_16_rb },
2366 { 2, 3, (hqx_func_t *)hq3x_16_rb },
2367 { 2, 4, (hqx_func_t *)hq4x_16_rb },
2368 { 3, 2, (hqx_func_t *)hq2x_24_rb },
2369 { 3, 3, (hqx_func_t *)hq3x_24_rb },
2370 { 3, 4, (hqx_func_t *)hq4x_24_rb },
2371 { 4, 2, (hqx_func_t *)hq2x_32_rb },
2372 { 4, 3, (hqx_func_t *)hq3x_32_rb },
2373 { 4, 4, (hqx_func_t *)hq4x_32_rb },
2375 static bool hqx_initialized = false;
2376 unsigned int width;
2377 unsigned int height;
2378 unsigned int x_off;
2379 unsigned int y_off;
2380 unsigned int x_scale;
2381 unsigned int y_scale;
2382 hqx_func_t *hqx;
2383 unsigned int i;
2385 if (out->failed == true) {
2386 failed:
2387 filter_off(in, out);
2388 return;
2390 if (out->updated == true) {
2391 hqx = *(hqx_func_t **)out->data;
2392 process:
2393 // Feed this to HQX.
2394 (*hqx)((void *)in->buf.u32, in->pitch,
2395 (void *)out->buf.u32, out->pitch,
2396 in->width, in->height);
2397 return;
2399 // Initialize filter.
2400 assert(out->data == NULL);
2401 x_scale = screen.x_scale;
2402 y_scale = screen.y_scale;
2403 retry:
2404 while ((width = (in->width * x_scale)) > out->width)
2405 --x_scale;
2406 while ((height = (in->height * y_scale)) > out->height)
2407 --y_scale;
2408 // Check whether output is large enough.
2409 if ((x_scale == 0) || (y_scale == 0)) {
2410 DEBUG(("cannot rescale by %ux%u", x_scale, y_scale));
2411 out->failed = true;
2412 goto failed;
2414 // Find a suitable combination.
2415 for (i = 0; (i != elemof(hqx_mode)); ++i)
2416 if ((hqx_mode[i].Bpp == screen.Bpp) &&
2417 (hqx_mode[i].scale == x_scale) &&
2418 (hqx_mode[i].scale == y_scale))
2419 break;
2420 if (i == elemof(hqx_mode)) {
2421 // Nothing matches, find something that fits.
2422 DEBUG(("%ux%u @%u Bpp scale factor not supported, trying"
2423 " another",
2424 x_scale, y_scale, screen.Bpp));
2425 // Must be square.
2426 if (x_scale > y_scale)
2427 x_scale = y_scale;
2428 else if (y_scale > x_scale)
2429 y_scale = x_scale;
2430 assert(x_scale == y_scale);
2431 (void)y_scale;
2432 do {
2433 --i;
2434 if ((hqx_mode[i].Bpp == screen.Bpp) &&
2435 (hqx_mode[i].scale <= x_scale)) {
2436 x_scale = hqx_mode[i].scale;
2437 y_scale = hqx_mode[i].scale;
2438 goto retry;
2441 while (i != 0);
2442 DEBUG(("failed to use %ux%u @%u Bpp scale factor",
2443 x_scale, y_scale, screen.Bpp));
2444 out->failed = true;
2445 goto failed;
2447 DEBUG(("using %ux%u @%u Bpp scale factor",
2448 x_scale, y_scale, screen.Bpp));
2449 hqx = hqx_mode[i].func;
2450 out->data = malloc(sizeof(hqx));
2451 if (out->data == NULL) {
2452 DEBUG(("allocation failure"));
2453 out->failed = true;
2454 goto failed;
2456 *(hqx_func_t **)out->data = hqx;
2457 // Center output.
2458 x_off = ((out->width - width) / 2);
2459 y_off = ((out->height - height) / 2);
2460 out->buf.u8 += (x_off * screen.Bpp);
2461 out->buf.u8 += (out->pitch * y_off);
2462 out->width = width;
2463 out->height = height;
2464 out->updated = true;
2465 // Initialize HQX if necessary.
2466 if (hqx_initialized == false) {
2467 pd_message_cursor(~0u, "Initializing hqx...");
2468 stopped = 1;
2469 hqxInit();
2470 pd_message_cursor(~0u, "");
2471 hqx_initialized = true;
2473 goto process;
2476 #endif // WITH_HQX
2478 #ifdef WITH_SCALE2X
2481 * This filter attempts to rescale with Scale2x.
2482 * @param in Input buffer data.
2483 * @param out Output buffer data.
2485 static void filter_scale2x(const struct filter_data *in,
2486 struct filter_data *out)
2488 static const struct {
2489 unsigned int x_scale;
2490 unsigned int y_scale;
2491 unsigned int mode;
2492 } scale2x_mode[] = {
2493 { 2, 2, 2 },
2494 { 2, 3, 203 },
2495 { 2, 4, 204 },
2496 { 3, 3, 3 },
2497 { 4, 4, 4 }
2499 unsigned int width;
2500 unsigned int height;
2501 unsigned int x_off;
2502 unsigned int y_off;
2503 unsigned int x_scale;
2504 unsigned int y_scale;
2505 unsigned int mode;
2506 unsigned int i;
2508 if (out->failed == true) {
2509 failed:
2510 filter_off(in, out);
2511 return;
2513 if (out->updated == true) {
2514 mode = *(unsigned int *)out->data;
2515 process:
2516 // Feed this to scale2x.
2517 scale(mode, out->buf.u32, out->pitch, in->buf.u32, in->pitch,
2518 screen.Bpp, in->width, in->height);
2519 return;
2521 // Initialize filter.
2522 assert(out->data == NULL);
2523 x_scale = screen.x_scale;
2524 y_scale = screen.y_scale;
2525 retry:
2526 while ((width = (in->width * x_scale)) > out->width)
2527 --x_scale;
2528 while ((height = (in->height * y_scale)) > out->height)
2529 --y_scale;
2530 // Check whether output is large enough.
2531 if ((x_scale == 0) || (y_scale == 0)) {
2532 DEBUG(("cannot rescale by %ux%u", x_scale, y_scale));
2533 out->failed = true;
2534 goto failed;
2536 // Check whether depth is supported by filter.
2537 if ((screen.Bpp != 4) && (screen.Bpp != 2)) {
2538 DEBUG(("unsupported depth %u", screen.bpp));
2539 out->failed = true;
2540 goto failed;
2542 // Find a suitable combination.
2543 for (i = 0; (i != elemof(scale2x_mode)); ++i)
2544 if ((scale2x_mode[i].x_scale == x_scale) &&
2545 (scale2x_mode[i].y_scale == y_scale))
2546 break;
2547 if (i == elemof(scale2x_mode)) {
2548 // Nothing matches, find something that fits.
2549 DEBUG(("%ux%u scale factor not supported, trying another",
2550 x_scale, y_scale));
2551 do {
2552 --i;
2553 if ((scale2x_mode[i].x_scale <= x_scale) &&
2554 (scale2x_mode[i].y_scale <= y_scale)) {
2555 x_scale = scale2x_mode[i].x_scale;
2556 y_scale = scale2x_mode[i].y_scale;
2557 goto retry;
2560 while (i != 0);
2561 DEBUG(("failed to use %ux%u scale factor", x_scale, y_scale));
2562 out->failed = true;
2563 goto failed;
2565 DEBUG(("using %ux%u scale factor", x_scale, y_scale));
2566 mode = scale2x_mode[i].mode;
2567 out->data = malloc(sizeof(mode));
2568 if (out->data == NULL) {
2569 DEBUG(("allocation failure"));
2570 out->failed = true;
2571 goto failed;
2573 *(unsigned int *)out->data = mode;
2574 // Center output.
2575 x_off = ((out->width - width) / 2);
2576 y_off = ((out->height - height) / 2);
2577 out->buf.u8 += (x_off * screen.Bpp);
2578 out->buf.u8 += (out->pitch * y_off);
2579 out->width = width;
2580 out->height = height;
2581 out->updated = true;
2582 goto process;
2585 #endif // WITH_SCALE2X
2587 #ifdef WITH_CTV
2589 // "Blur" CTV filters.
2591 static void filter_blur_32(const struct filter_data *in,
2592 struct filter_data *out)
2594 bpp_t in_buf = in->buf;
2595 bpp_t out_buf = out->buf;
2596 unsigned int xsize = out->width;
2597 unsigned int ysize = out->height;
2598 unsigned int y;
2600 for (y = 0; (y < ysize); ++y) {
2601 uint32_t old = *in_buf.u32;
2602 unsigned int x;
2604 for (x = 0; (x < xsize); ++x) {
2605 uint32_t tmp = in_buf.u32[x];
2607 tmp = (((((tmp & 0x00ff00ff) +
2608 (old & 0x00ff00ff)) >> 1) & 0x00ff00ff) |
2609 ((((tmp & 0xff00ff00) +
2610 (old & 0xff00ff00)) >> 1) & 0xff00ff00));
2611 old = in_buf.u32[x];
2612 out_buf.u32[x] = tmp;
2614 in_buf.u8 += in->pitch;
2615 out_buf.u8 += out->pitch;
2619 static void filter_blur_24(const struct filter_data *in,
2620 struct filter_data *out)
2622 bpp_t in_buf = in->buf;
2623 bpp_t out_buf = out->buf;
2624 unsigned int xsize = out->width;
2625 unsigned int ysize = out->height;
2626 unsigned int y;
2628 for (y = 0; (y < ysize); ++y) {
2629 uint24_t old;
2630 unsigned int x;
2632 u24cpy(&old, in_buf.u24);
2633 for (x = 0; (x < xsize); ++x) {
2634 uint24_t tmp;
2636 u24cpy(&tmp, &in_buf.u24[x]);
2637 out_buf.u24[x][0] = ((tmp[0] + old[0]) >> 1);
2638 out_buf.u24[x][1] = ((tmp[1] + old[1]) >> 1);
2639 out_buf.u24[x][2] = ((tmp[2] + old[2]) >> 1);
2640 u24cpy(&old, &tmp);
2642 in_buf.u8 += in->pitch;
2643 out_buf.u8 += out->pitch;
2647 static void filter_blur_16(const struct filter_data *in,
2648 struct filter_data *out)
2650 bpp_t in_buf = in->buf;
2651 bpp_t out_buf = out->buf;
2652 unsigned int xsize = out->width;
2653 unsigned int ysize = out->height;
2654 unsigned int y;
2656 #ifdef WITH_X86_CTV
2657 if (in_buf.u16 == out_buf.u16) {
2658 for (y = 0; (y < ysize); ++y) {
2659 // Blur, by Dave
2660 blur_bitmap_16((uint8_t *)out_buf.u16, (xsize - 1));
2661 out_buf.u8 += out->pitch;
2663 return;
2665 #endif
2666 for (y = 0; (y < ysize); ++y) {
2667 uint16_t old = *in_buf.u16;
2668 unsigned int x;
2670 for (x = 0; (x < xsize); ++x) {
2671 uint16_t tmp = in_buf.u16[x];
2673 tmp = (((((tmp & 0xf81f) +
2674 (old & 0xf81f)) >> 1) & 0xf81f) |
2675 ((((tmp & 0x07e0) +
2676 (old & 0x07e0)) >> 1) & 0x07e0));
2677 old = in_buf.u16[x];
2678 out_buf.u16[x] = tmp;
2680 in_buf.u8 += in->pitch;
2681 out_buf.u8 += out->pitch;
2685 static void filter_blur_15(const struct filter_data *in,
2686 struct filter_data *out)
2688 bpp_t in_buf = in->buf;
2689 bpp_t out_buf = out->buf;
2690 unsigned int xsize = out->width;
2691 unsigned int ysize = out->height;
2692 unsigned int y;
2694 #ifdef WITH_X86_CTV
2695 if (in_buf.u15 == out_buf.u15) {
2696 for (y = 0; (y < ysize); ++y) {
2697 // Blur, by Dave
2698 blur_bitmap_15((uint8_t *)out_buf.u15, (xsize - 1));
2699 out_buf.u8 += out->pitch;
2701 return;
2703 #endif
2704 for (y = 0; (y < ysize); ++y) {
2705 uint16_t old = *in_buf.u15;
2706 unsigned int x;
2708 for (x = 0; (x < xsize); ++x) {
2709 uint16_t tmp = in_buf.u15[x];
2711 tmp = (((((tmp & 0x7c1f) +
2712 (old & 0x7c1f)) >> 1) & 0x7c1f) |
2713 ((((tmp & 0x03e0) +
2714 (old & 0x03e0)) >> 1) & 0x03e0));
2715 old = in_buf.u15[x];
2716 out_buf.u15[x] = tmp;
2718 in_buf.u8 += in->pitch;
2719 out_buf.u8 += out->pitch;
2723 static void filter_blur(const struct filter_data *in,
2724 struct filter_data *out)
2726 static const struct {
2727 unsigned int bpp;
2728 filter_func_t *filter;
2729 } blur_mode[] = {
2730 { 32, filter_blur_32 },
2731 { 24, filter_blur_24 },
2732 { 16, filter_blur_16 },
2733 { 15, filter_blur_15 },
2735 filter_func_t *blur;
2737 if (out->failed == true) {
2738 failed:
2739 filter_off(in, out);
2740 return;
2742 if (out->updated == false) {
2743 unsigned int i;
2745 for (i = 0; (i != elemof(blur_mode)); ++i)
2746 if (blur_mode[i].bpp == screen.bpp)
2747 break;
2748 if (i == elemof(blur_mode)) {
2749 DEBUG(("%u bpp depth is not supported", screen.bpp));
2750 out->failed = true;
2751 goto failed;
2753 blur = blur_mode[i].filter;
2754 out->data = malloc(sizeof(filter));
2755 if (out->data == NULL) {
2756 DEBUG(("allocation failure"));
2757 out->failed = true;
2758 goto failed;
2760 if (in->width <= out->width) {
2761 unsigned int x_off = ((out->width - in->width) / 2);
2762 unsigned int y_off = ((out->height - in->height) / 2);
2764 out->buf.u8 += (x_off * screen.Bpp);
2765 out->buf.u8 += (out->pitch * y_off);
2766 out->width = in->width;
2768 if (in->height <= out->height)
2769 out->height = in->height;
2770 *((filter_func_t **)out->data) = blur;
2771 out->updated = true;
2773 else
2774 blur = *(filter_func_t **)out->data;
2775 (*blur)(in, out);
2778 // Scanline/Interlace CTV filters.
2780 static void filter_scanline_frame(const struct filter_data *in,
2781 struct filter_data *out)
2783 unsigned int frame = ((unsigned int *)out->data)[0];
2784 unsigned int bpp = ((unsigned int *)out->data)[1];
2785 bpp_t in_buf = in->buf;
2786 bpp_t out_buf = out->buf;
2787 unsigned int xsize = out->width;
2788 unsigned int ysize = out->height;
2790 out_buf.u8 += (out->pitch * !!frame);
2791 switch (bpp) {
2792 unsigned int x;
2793 unsigned int y;
2795 case 32:
2796 for (y = frame; (y < ysize); y += 2) {
2797 for (x = 0; (x < xsize); ++x)
2798 out_buf.u32[x] =
2799 ((in_buf.u32[x] >> 1) & 0x7f7f7f7f);
2800 in_buf.u8 += (in->pitch * 2);
2801 out_buf.u8 += (out->pitch * 2);
2803 break;
2804 case 24:
2805 for (y = frame; (y < ysize); y += 2) {
2806 for (x = 0; (x < xsize); ++x) {
2807 out_buf.u24[x][0] = (in_buf.u24[x][0] >> 1);
2808 out_buf.u24[x][1] = (in_buf.u24[x][1] >> 1);
2809 out_buf.u24[x][2] = (in_buf.u24[x][2] >> 1);
2811 in_buf.u8 += (in->pitch * 2);
2812 out_buf.u8 += (out->pitch * 2);
2814 break;
2815 case 16:
2816 for (y = frame; (y < ysize); y += 2) {
2817 #ifdef WITH_X86_CTV
2818 if (in_buf.u16 == out_buf.u16) {
2819 // Scanline, by Phil
2820 test_ctv((uint8_t *)out_buf.u16, xsize);
2822 else
2823 #endif
2824 for (x = 0; (x < xsize); ++x)
2825 out_buf.u16[x] =
2826 ((in_buf.u16[x] >> 1) & 0x7bef);
2827 in_buf.u8 += (in->pitch * 2);
2828 out_buf.u8 += (out->pitch * 2);
2830 break;
2831 case 15:
2832 for (y = frame; (y < ysize); y += 2) {
2833 #ifdef WITH_X86_CTV
2834 if (in_buf.u15 == out_buf.u15) {
2835 // Scanline, by Phil
2836 test_ctv((uint8_t *)out_buf.u16, xsize);
2838 else
2839 #endif
2840 for (x = 0; (x < xsize); ++x)
2841 out_buf.u15[x] =
2842 ((in_buf.u15[x] >> 1) & 0x3def);
2843 in_buf.u8 += (in->pitch * 2);
2844 out_buf.u8 += (out->pitch * 2);
2846 break;
2850 static void filter_scanline(const struct filter_data *in,
2851 struct filter_data *out)
2853 if (out->failed == true) {
2854 failed:
2855 filter_off(in, out);
2856 return;
2858 if (out->updated == false) {
2859 if ((screen.bpp != 32) &&
2860 (screen.bpp != 24) &&
2861 (screen.bpp != 16) &&
2862 (screen.bpp != 15)) {
2863 DEBUG(("%u bpp depth is not supported", screen.bpp));
2864 out->failed = true;
2865 goto failed;
2867 out->data = malloc(sizeof(unsigned int [2]));
2868 if (out->data == NULL) {
2869 DEBUG(("allocation failure"));
2870 out->failed = true;
2871 goto failed;
2873 if (in->width <= out->width) {
2874 unsigned int x_off = ((out->width - in->width) / 2);
2875 unsigned int y_off = ((out->height - in->height) / 2);
2877 out->buf.u8 += (x_off * screen.Bpp);
2878 out->buf.u8 += (out->pitch * y_off);
2879 out->width = in->width;
2881 if (in->height <= out->height)
2882 out->height = in->height;
2883 ((unsigned int *)out->data)[0] = 0;
2884 ((unsigned int *)out->data)[1] = screen.bpp;
2885 out->updated = true;
2887 filter_scanline_frame(in, out);
2890 static void filter_interlace(const struct filter_data *in,
2891 struct filter_data *out)
2893 if (out->failed == true) {
2894 failed:
2895 filter_off(in, out);
2896 return;
2898 if (out->updated == false) {
2899 if ((screen.bpp != 32) &&
2900 (screen.bpp != 24) &&
2901 (screen.bpp != 16) &&
2902 (screen.bpp != 15)) {
2903 DEBUG(("%u bpp depth is not supported", screen.bpp));
2904 out->failed = true;
2905 goto failed;
2907 out->data = malloc(sizeof(unsigned int [2]));
2908 if (out->data == NULL) {
2909 DEBUG(("allocation failure"));
2910 out->failed = true;
2911 goto failed;
2913 if (in->width <= out->width) {
2914 unsigned int x_off = ((out->width - in->width) / 2);
2915 unsigned int y_off = ((out->height - in->height) / 2);
2917 out->buf.u8 += (x_off * screen.Bpp);
2918 out->buf.u8 += (out->pitch * y_off);
2919 out->width = in->width;
2921 if (in->height <= out->height)
2922 out->height = in->height;
2923 ((unsigned int *)out->data)[0] = 0;
2924 ((unsigned int *)out->data)[1] = screen.bpp;
2925 out->updated = true;
2927 filter_scanline_frame(in, out);
2928 ((unsigned int *)out->data)[0] ^= 1;
2931 // Byte swap filter.
2932 static void filter_swab(const struct filter_data *in,
2933 struct filter_data *out)
2935 bpp_t in_buf;
2936 bpp_t out_buf;
2937 unsigned int xsize;
2938 unsigned int ysize;
2940 if (out->failed == true) {
2941 failed:
2942 filter_off(in, out);
2943 return;
2945 if (out->updated == false) {
2946 if ((screen.Bpp != 4) &&
2947 (screen.Bpp != 3) &&
2948 (screen.Bpp != 2)) {
2949 DEBUG(("%u Bpp depth is not supported", screen.Bpp));
2950 out->failed = true;
2951 goto failed;
2953 if (in->width <= out->width) {
2954 unsigned int x_off = ((out->width - in->width) / 2);
2955 unsigned int y_off = ((out->height - in->height) / 2);
2957 out->buf.u8 += (x_off * screen.Bpp);
2958 out->buf.u8 += (out->pitch * y_off);
2959 out->width = in->width;
2961 if (in->height <= out->height)
2962 out->height = in->height;
2963 out->updated = true;
2965 in_buf = in->buf;
2966 out_buf = out->buf;
2967 ysize = out->height;
2968 xsize = out->width;
2969 switch (screen.Bpp) {
2970 unsigned int x;
2971 unsigned int y;
2973 case 4:
2974 for (y = 0; (y < ysize); ++y) {
2975 for (x = 0; (x < xsize); ++x) {
2976 union {
2977 uint32_t u32;
2978 uint8_t u8[4];
2979 } tmp[2];
2981 tmp[0].u32 = in_buf.u32[x];
2982 tmp[1].u8[0] = tmp[0].u8[3];
2983 tmp[1].u8[1] = tmp[0].u8[2];
2984 tmp[1].u8[2] = tmp[0].u8[1];
2985 tmp[1].u8[3] = tmp[0].u8[0];
2986 out_buf.u32[x] = tmp[1].u32;
2988 in_buf.u8 += in->pitch;
2989 out_buf.u8 += out->pitch;
2991 break;
2992 case 3:
2993 for (y = 0; (y < ysize); ++y) {
2994 for (x = 0; (x < xsize); ++x) {
2995 uint24_t tmp = {
2996 in_buf.u24[x][2],
2997 in_buf.u24[x][1],
2998 in_buf.u24[x][0]
3001 u24cpy(&out_buf.u24[x], &tmp);
3003 in_buf.u8 += in->pitch;
3004 out_buf.u8 += out->pitch;
3006 break;
3007 case 2:
3008 for (y = 0; (y < ysize); ++y) {
3009 for (x = 0; (x < xsize); ++x)
3010 out_buf.u16[x] = ((in_buf.u16[x] << 8) |
3011 (in_buf.u16[x] >> 8));
3012 in_buf.u8 += in->pitch;
3013 out_buf.u8 += out->pitch;
3015 break;
3019 #endif // WITH_CTV
3022 * Special characters interpreted by filter_text().
3023 * FILTER_TEXT_BG_NONE transparent background.
3024 * FILTER_TEXT_BG_BLACK black background.
3025 * FILTER_TEXT_7X6 use 7x6 font.
3026 * FILTER_TEXT_8X13 use 8x13 font.
3027 * FILTER_TEXT_16X26 use 16x26 font.
3028 * FILTER_TEXT_CENTER center justify.
3029 * FILTER_TEXT_LEFT left justify.
3030 * FILTER_TEXT_RIGHT right justify.
3032 #define FILTER_TEXT_ESCAPE "\033"
3033 #define FILTER_TEXT_BG_NONE FILTER_TEXT_ESCAPE "\x01\x01"
3034 #define FILTER_TEXT_BG_BLACK FILTER_TEXT_ESCAPE "\x01\x02"
3035 #define FILTER_TEXT_7X6 FILTER_TEXT_ESCAPE "\x02\x01"
3036 #define FILTER_TEXT_8X13 FILTER_TEXT_ESCAPE "\x02\x02"
3037 #define FILTER_TEXT_16X26 FILTER_TEXT_ESCAPE "\x02\x03"
3038 #define FILTER_TEXT_CENTER FILTER_TEXT_ESCAPE "\x03\x01"
3039 #define FILTER_TEXT_LEFT FILTER_TEXT_ESCAPE "\x03\x02"
3040 #define FILTER_TEXT_RIGHT FILTER_TEXT_ESCAPE "\x03\x03"
3042 static char filter_text_str[2048];
3045 * Append message to filter_text_str[].
3047 static void filter_text_msg(const char *fmt, ...)
3049 size_t off;
3050 size_t len = sizeof(filter_text_str);
3051 va_list vl;
3053 assert(filter_text_str[(len - 1)] == '\0');
3054 off = strlen(filter_text_str);
3055 len -= off;
3056 if (len == 0)
3057 return;
3058 va_start(vl, fmt);
3059 vsnprintf(&filter_text_str[off], len, fmt, vl);
3060 va_end(vl);
3064 * Text overlay filter.
3065 * @param in Input buffer data.
3066 * @param out Output buffer data.
3068 static void filter_text(const struct filter_data *in,
3069 struct filter_data *out)
3071 bpp_t buf = out->buf;
3072 unsigned int buf_pitch = out->pitch;
3073 unsigned int xsize = out->width;
3074 unsigned int ysize = out->height;
3075 unsigned int bpp = screen.bpp;
3076 unsigned int Bpp = ((bpp + 1) / 8);
3077 const char *str = filter_text_str;
3078 const char *next = str;
3079 bool clear = false;
3080 bool flush = false;
3081 enum { LEFT, CENTER, RIGHT } justify = LEFT;
3082 const struct {
3083 enum font_type type;
3084 unsigned int width;
3085 unsigned int height;
3086 } font_data[] = {
3087 { FONT_TYPE_7X5, 7, (5 + 1) }, // +1 for vertical spacing.
3088 { FONT_TYPE_8X13, 8, 13 },
3089 { FONT_TYPE_16X26, 16, 26 }
3090 }, *font = &font_data[0], *old_font = font;
3091 unsigned int line_length = 0;
3092 unsigned int line_off = 0;
3093 unsigned int line_width = 0;
3094 unsigned int line_height = font->height;
3096 // Input is unused.
3097 (void)in;
3098 assert(filter_text_str[(sizeof(filter_text_str) - 1)] == '\0');
3099 while (1) {
3100 unsigned int len;
3101 unsigned int width;
3103 if ((*next == '\0') || (*next == '\n')) {
3104 trunc:
3105 if (flush == false) {
3106 next = str;
3107 assert(line_width <= xsize);
3108 switch (justify) {
3109 case LEFT:
3110 line_off = 0;
3111 break;
3112 case CENTER:
3113 line_off = ((xsize - line_width) / 2);
3114 break;
3115 case RIGHT:
3116 line_off = (xsize - line_width);
3117 break;
3119 if (clear)
3120 memset(buf.u8, 0,
3121 (buf_pitch * line_height));
3122 font = old_font;
3123 flush = true;
3125 else if (*next == '\0')
3126 break;
3127 else {
3128 if (*next == '\n')
3129 ++next;
3130 str = next;
3131 old_font = font;
3132 line_length = 0;
3133 line_off = 0;
3134 line_width = 0;
3135 buf.u8 += (buf_pitch * line_height);
3136 ysize -= line_height;
3137 line_height = font->height;
3138 // Still enough vertical pixels for this line?
3139 if (ysize < line_height)
3140 break;
3141 flush = false;
3144 else if (*next == *FILTER_TEXT_ESCAPE) {
3145 const char *tmp;
3146 size_t sz;
3148 #define FILTER_TEXT_IS(f) \
3149 (tmp = (f), sz = strlen(f), \
3150 !strncmp(tmp, next, sz))
3152 if (FILTER_TEXT_IS(FILTER_TEXT_BG_NONE))
3153 clear = false;
3154 else if (FILTER_TEXT_IS(FILTER_TEXT_BG_BLACK))
3155 clear = true;
3156 else if (FILTER_TEXT_IS(FILTER_TEXT_CENTER))
3157 justify = CENTER;
3158 else if (FILTER_TEXT_IS(FILTER_TEXT_LEFT))
3159 justify = LEFT;
3160 else if (FILTER_TEXT_IS(FILTER_TEXT_RIGHT))
3161 justify = RIGHT;
3162 else if (FILTER_TEXT_IS(FILTER_TEXT_7X6))
3163 font = &font_data[0];
3164 else if (FILTER_TEXT_IS(FILTER_TEXT_8X13))
3165 font = &font_data[1];
3166 else if (FILTER_TEXT_IS(FILTER_TEXT_16X26))
3167 font = &font_data[2];
3168 next += sz;
3170 else if ((line_width + font->width) <= xsize) {
3171 ++line_length;
3172 line_width += font->width;
3173 if (line_height < font->height) {
3174 line_height = font->height;
3175 // Still enough vertical pixels for this line?
3176 if (ysize < line_height)
3177 break;
3179 ++next;
3181 else // Truncate line.
3182 goto trunc;
3183 if (flush == false)
3184 continue;
3185 // Compute number of characters and width.
3186 len = 0;
3187 width = 0;
3188 while ((len != line_length) &&
3189 (next[len] != '\0') &&
3190 (next[len] != '\n') &&
3191 (next[len] != *FILTER_TEXT_ESCAPE)) {
3192 width += font->width;
3193 ++len;
3195 // Display.
3196 len = font_text((buf.u8 +
3197 // Horizontal offset.
3198 (line_off * Bpp) +
3199 // Vertical offset.
3200 ((line_height - font->height) * buf_pitch)),
3201 (xsize - line_off),
3202 line_height, Bpp, buf_pitch, next, len, ~0u,
3203 font->type);
3204 line_off += width;
3205 next += len;
3209 static const struct filter filter_text_def = {
3210 "text", filter_text, true, false, false
3213 #ifdef WITH_CTV
3215 static void set_swab()
3217 const struct filter *f = filters_find("swab");
3219 if (f == NULL)
3220 return;
3221 filters_pluck(f);
3222 if (dgen_swab)
3223 filters_insert(f);
3226 static int prompt_cmd_filter_push(class md&, unsigned int ac, const char** av)
3228 unsigned int i;
3230 if (ac < 2)
3231 return CMD_EINVAL;
3232 for (i = 1; (i != ac); ++i) {
3233 const struct filter *f = filters_find(av[i]);
3235 if (f == NULL)
3236 return CMD_EINVAL;
3237 filters_push(f);
3239 return CMD_OK;
3242 static char* prompt_cmpl_filter_push(class md&, unsigned int ac,
3243 const char** av, unsigned int len)
3245 const struct filter *f;
3246 const char *prefix;
3247 unsigned int skip;
3248 unsigned int i;
3250 assert(ac != 0);
3251 if ((ac == 1) || (len == ~0u) || (av[(ac - 1)] == NULL)) {
3252 prefix = "";
3253 len = 0;
3255 else
3256 prefix = av[(ac - 1)];
3257 skip = prompt.skip;
3258 retry:
3259 for (i = 0; (i != elemof(filters_available)); ++i) {
3260 f = &filters_available[i];
3261 if (strncasecmp(prefix, f->name, len))
3262 continue;
3263 if (skip == 0)
3264 break;
3265 --skip;
3267 if (i == elemof(filters_available)) {
3268 if (prompt.skip != 0) {
3269 prompt.skip = 0;
3270 goto retry;
3272 return NULL;
3274 ++prompt.skip;
3275 return strdup(f->name);
3278 static int prompt_cmd_filter_pop(class md&, unsigned int ac, const char**)
3280 if (ac != 1)
3281 return CMD_EINVAL;
3282 filters_pop();
3283 return CMD_OK;
3286 static int prompt_cmd_filter_none(class md&, unsigned int ac, const char**)
3288 if (ac != 1)
3289 return CMD_EINVAL;
3290 filters_empty();
3291 return CMD_OK;
3294 #endif // WITH_CTV
3296 static bool calibrating = false; //< True during calibration.
3297 static unsigned int calibrating_controller; ///< Controller being calibrated.
3299 static void manage_calibration(enum rc_binding_type type, intptr_t code);
3302 * Interactively calibrate a controller.
3303 * If n_args == 1, controller 0 will be configured.
3304 * If n_args == 2, configure controller in string args[1].
3305 * @param n_args Number of arguments.
3306 * @param[in] args List of arguments.
3307 * @return Status code.
3309 static int
3310 prompt_cmd_calibrate(class md&, unsigned int n_args, const char** args)
3312 /* check args first */
3313 if (n_args == 1)
3314 calibrating_controller = 0;
3315 else if (n_args == 2) {
3316 calibrating_controller = (atoi(args[1]) - 1);
3317 if (calibrating_controller > 1)
3318 return CMD_EINVAL;
3320 else
3321 return CMD_EINVAL;
3322 manage_calibration(RCB_NUM, -1);
3323 return (CMD_OK | CMD_MSG);
3326 static int set_scaling(const char *name)
3328 unsigned int i = filters_stack_size;
3330 assert(i <= elemof(filters_stack));
3331 // Replace all current scalers with these.
3332 while (i != 0) {
3333 --i;
3334 if (filters_stack[i]->resize == true)
3335 filters_remove(i);
3337 while (name += strspn(name, " \t\n"), name[0] != '\0') {
3338 const struct filter *f;
3339 int len = strcspn(name, " \t\n");
3340 char token[64];
3342 snprintf(token, sizeof(token), "%.*s", len, name);
3343 name += len;
3344 if (((f = filters_find(token)) == NULL) ||
3345 (filters_stack_size == elemof(filters_stack)))
3346 return -1;
3347 filters_push(f);
3349 return 0;
3353 * Display splash screen.
3355 static void mdscr_splash()
3357 unsigned int x;
3358 unsigned int y;
3359 bpp_t src;
3360 unsigned int src_pitch = (dgen_splash_data.width *
3361 dgen_splash_data.bytes_per_pixel);
3362 unsigned int sw = dgen_splash_data.width;
3363 unsigned int sh = dgen_splash_data.height;
3364 bpp_t dst;
3365 unsigned int dst_pitch = mdscr.pitch;
3366 unsigned int dw = video.width;
3367 unsigned int dh = video.height;
3369 if ((dgen_splash_data.bytes_per_pixel != 3) || (sw != dw))
3370 return;
3371 src.u8 = (uint8_t *)dgen_splash_data.pixel_data;
3372 dst.u8 = ((uint8_t *)mdscr.data + (dst_pitch * 8) + 16);
3373 // Center it.
3374 if (sh < dh) {
3375 unsigned int off = ((dh - sh) / 2);
3377 memset(dst.u8, 0x00, (dst_pitch * off));
3378 memset(&dst.u8[(dst_pitch * (off + sh))], 0x00,
3379 (dst_pitch * (dh - (off + sh))));
3380 dst.u8 += (dst_pitch * off);
3382 switch (mdscr.bpp) {
3383 case 32:
3384 for (y = 0; ((y != dh) && (y != sh)); ++y) {
3385 for (x = 0; ((x != dw) && (x != sw)); ++x) {
3386 dst.u32[x] = ((src.u24[x][0] << 16) |
3387 (src.u24[x][1] << 8) |
3388 (src.u24[x][2] << 0));
3390 src.u8 += src_pitch;
3391 dst.u8 += dst_pitch;
3393 break;
3394 case 24:
3395 for (y = 0; ((y != dh) && (y != sh)); ++y) {
3396 for (x = 0; ((x != dw) && (x != sw)); ++x) {
3397 dst.u24[x][0] = src.u24[x][2];
3398 dst.u24[x][1] = src.u24[x][1];
3399 dst.u24[x][2] = src.u24[x][0];
3401 src.u8 += src_pitch;
3402 dst.u8 += dst_pitch;
3404 break;
3405 case 16:
3406 for (y = 0; ((y != dh) && (y != sh)); ++y) {
3407 for (x = 0; ((x != dw) && (x != sw)); ++x) {
3408 dst.u16[x] = (((src.u24[x][0] & 0xf8) << 8) |
3409 ((src.u24[x][1] & 0xfc) << 3) |
3410 ((src.u24[x][2] & 0xf8) >> 3));
3412 src.u8 += src_pitch;
3413 dst.u8 += dst_pitch;
3415 break;
3416 case 15:
3417 for (y = 0; ((y != dh) && (y != sh)); ++y) {
3418 for (x = 0; ((x != dw) && (x != sw)); ++x) {
3419 dst.u16[x] = (((src.u24[x][0] & 0xf8) << 7) |
3420 ((src.u24[x][1] & 0xf8) << 2) |
3421 ((src.u24[x][2] & 0xf8) >> 3));
3423 src.u8 += src_pitch;
3424 dst.u8 += dst_pitch;
3426 break;
3427 case 8:
3428 break;
3433 * Initialize screen.
3435 * @param width Width of display.
3436 * @param height Height of display.
3437 * @return 0 on success, -1 if screen could not be initialized with current
3438 * options but remains in its previous state, -2 if screen is unusable.
3440 static int screen_init(unsigned int width, unsigned int height)
3442 static bool once = true;
3443 uint32_t flags = (SDL_RESIZABLE | SDL_ANYFORMAT | SDL_HWPALETTE |
3444 SDL_HWSURFACE);
3445 struct screen scrtmp;
3446 const struct dgen_font *font;
3448 #ifdef WITH_THREADS
3449 screen_update_thread_stop();
3450 #endif
3451 DEBUG(("want width=%u height=%u", width, height));
3452 stopped = 1;
3453 // Copy current screen data.
3454 memcpy(&scrtmp, &screen, sizeof(scrtmp));
3455 if (once) {
3456 unsigned int info_height = dgen_font[FONT_TYPE_8X13].h;
3458 // Force defaults once.
3459 scrtmp.window_width = 0;
3460 scrtmp.window_height = 0;
3461 scrtmp.width = (video.width * 2);
3462 scrtmp.height = ((video.height * 2) + info_height);
3463 scrtmp.x_scale = (scrtmp.width / video.width);
3464 scrtmp.y_scale = (scrtmp.height / video.height);
3465 scrtmp.bpp = 0;
3466 scrtmp.Bpp = 0;
3467 scrtmp.info_height = info_height;
3468 scrtmp.buf.u8 = 0;
3469 scrtmp.pitch = 0;
3470 scrtmp.surface = 0;
3471 scrtmp.want_fullscreen = 0;
3472 scrtmp.is_fullscreen = 0;
3473 #ifdef WITH_OPENGL
3474 scrtmp.want_opengl = 0;
3475 scrtmp.is_opengl = 0;
3476 #endif
3477 #ifdef WITH_THREADS
3478 scrtmp.want_thread = 0;
3479 scrtmp.is_thread = 0;
3480 scrtmp.thread = 0;
3481 scrtmp.lock = 0;
3482 scrtmp.cond = 0;
3483 #endif
3484 memset(scrtmp.color, 0, sizeof(scrtmp.color));
3485 once = false;
3487 // Use configuration data.
3488 if (width != 0)
3489 scrtmp.width = width;
3490 if (dgen_width >= 1)
3491 scrtmp.width = dgen_width;
3492 if (height != 0)
3493 scrtmp.height = height;
3494 if (dgen_height >= 1)
3495 scrtmp.height = dgen_height;
3496 if (dgen_depth >= 0) {
3497 scrtmp.bpp = dgen_depth;
3498 scrtmp.Bpp = 0;
3500 // scrtmp.x_scale, scrtmp.y_scale and scrtmp.info_height cannot be
3501 // determined yet.
3502 scrtmp.want_fullscreen = !!dgen_fullscreen;
3503 #ifdef WITH_OPENGL
3504 opengl_failed:
3505 scrtmp.want_opengl = !!dgen_opengl;
3506 #endif
3507 #ifdef WITH_THREADS
3508 scrtmp.want_thread = !!dgen_screen_thread;
3509 #endif
3510 // Configure SDL_SetVideoMode().
3511 if (scrtmp.want_fullscreen)
3512 flags |= SDL_FULLSCREEN;
3513 #ifdef WITH_OPENGL
3514 if (scrtmp.want_opengl) {
3515 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
3516 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
3517 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
3518 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
3519 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, !!dgen_doublebuffer);
3520 flags |= SDL_OPENGL;
3522 else
3523 #endif
3524 flags |= ((dgen_doublebuffer ? SDL_DOUBLEBUF : 0) |
3525 SDL_ASYNCBLIT);
3526 if (scrtmp.want_fullscreen) {
3527 SDL_Rect **modes;
3529 // Check if we're going to be bound to a particular resolution.
3530 modes = SDL_ListModes(NULL, (flags | SDL_FULLSCREEN));
3531 if ((modes != NULL) && (modes != (SDL_Rect **)-1)) {
3532 unsigned int i;
3533 struct {
3534 unsigned int i;
3535 unsigned int w;
3536 unsigned int h;
3537 } best = { 0, (unsigned int)-1, (unsigned int)-1 };
3539 // Find the best resolution available.
3540 for (i = 0; (modes[i] != NULL); ++i) {
3541 unsigned int w, h;
3543 DEBUG(("checking mode %dx%d",
3544 modes[i]->w, modes[i]->h));
3545 if ((modes[i]->w < scrtmp.width) ||
3546 (modes[i]->h < scrtmp.height))
3547 continue;
3548 w = (modes[i]->w - scrtmp.width);
3549 h = (modes[i]->h - scrtmp.height);
3550 if ((w <= best.w) && (h <= best.h)) {
3551 best.i = i;
3552 best.w = w;
3553 best.h = h;
3556 if ((best.w == (unsigned int)-1) ||
3557 (best.h == (unsigned int)-1))
3558 DEBUG(("no mode looks good"));
3559 else {
3560 scrtmp.width = modes[best.i]->w;
3561 scrtmp.height = modes[best.i]->h;
3562 DEBUG(("mode %ux%u looks okay",
3563 scrtmp.width, scrtmp.height));
3566 DEBUG(("adjusted fullscreen resolution to %ux%u",
3567 scrtmp.width, scrtmp.height));
3569 // Set video mode.
3570 DEBUG(("SDL_SetVideoMode(%u, %u, %d, 0x%08x)",
3571 scrtmp.width, scrtmp.height, scrtmp.bpp, flags));
3572 scrtmp.surface = SDL_SetVideoMode(scrtmp.width, scrtmp.height,
3573 scrtmp.bpp, flags);
3574 if (scrtmp.surface == NULL) {
3575 #ifdef WITH_OPENGL
3576 // Try again without OpenGL.
3577 if (flags & SDL_OPENGL) {
3578 assert(scrtmp.want_opengl);
3579 DEBUG(("OpenGL initialization failed, retrying"
3580 " without it."));
3581 dgen_opengl = 0;
3582 flags &= ~SDL_OPENGL;
3583 goto opengl_failed;
3585 #endif
3586 return -1;
3588 DEBUG(("SDL_SetVideoMode succeeded"));
3589 // Update with current values.
3590 scrtmp.window_width = scrtmp.surface->w;
3591 scrtmp.window_height = scrtmp.surface->h;
3592 scrtmp.width = scrtmp.window_width;
3593 scrtmp.height = scrtmp.window_height;
3594 // By default, using 5% of the vertical resolution for info bar ought
3595 // to be good enough for anybody. Pick something close.
3596 if (dgen_info_height < 0)
3597 scrtmp.info_height = ((scrtmp.height * 5) / 100);
3598 else
3599 scrtmp.info_height = dgen_info_height;
3600 if (scrtmp.info_height > scrtmp.height)
3601 scrtmp.info_height = scrtmp.height;
3602 font = font_select(scrtmp.width, scrtmp.info_height, FONT_TYPE_AUTO);
3603 if (font == NULL)
3604 scrtmp.info_height = 0;
3605 else
3606 scrtmp.info_height = font->h;
3607 assert(scrtmp.info_height <= scrtmp.height); // Do not forget.
3608 // Determine default X and Y scale values from what remains.
3609 if (dgen_x_scale >= 0)
3610 scrtmp.x_scale = dgen_x_scale;
3611 else
3612 scrtmp.x_scale = (scrtmp.width / video.width);
3613 if (dgen_y_scale >= 0)
3614 scrtmp.y_scale = dgen_y_scale;
3615 else
3616 scrtmp.y_scale = ((scrtmp.height - scrtmp.info_height) /
3617 video.height);
3618 if (dgen_aspect) {
3619 if (scrtmp.x_scale >= scrtmp.y_scale)
3620 scrtmp.x_scale = scrtmp.y_scale;
3621 else
3622 scrtmp.y_scale = scrtmp.x_scale;
3624 // Fix bpp.
3625 assert(scrtmp.surface->format != NULL);
3626 scrtmp.bpp = scrtmp.surface->format->BitsPerPixel;
3627 // 15 bpp has be forced if it was required. SDL does not return the
3628 // right value.
3629 if ((dgen_depth == 15) && (scrtmp.bpp == 16))
3630 scrtmp.bpp = 15;
3631 scrtmp.Bpp = scrtmp.surface->format->BytesPerPixel;
3632 scrtmp.buf.u8 = (uint8_t *)scrtmp.surface->pixels;
3633 scrtmp.pitch = scrtmp.surface->pitch;
3634 scrtmp.is_fullscreen = scrtmp.want_fullscreen;
3635 DEBUG(("video configuration: x_scale=%u y_scale=%u",
3636 scrtmp.x_scale, scrtmp.y_scale));
3637 DEBUG(("screen configuration: width=%u height=%u bpp=%u Bpp=%u"
3638 " info_height=%u"
3639 " buf.u8=%p pitch=%u surface=%p want_fullscreen=%u"
3640 " is_fullscreen=%u",
3641 scrtmp.width, scrtmp.height, scrtmp.bpp, scrtmp.Bpp,
3642 scrtmp.info_height,
3643 (void *)scrtmp.buf.u8, scrtmp.pitch, (void *)scrtmp.surface,
3644 scrtmp.want_fullscreen, scrtmp.is_fullscreen));
3645 #ifdef WITH_OPENGL
3646 if (scrtmp.want_opengl) {
3647 if (init_texture(&scrtmp)) {
3648 DEBUG(("OpenGL initialization failed, retrying"
3649 " without it."));
3650 dgen_opengl = 0;
3651 flags &= ~SDL_OPENGL;
3652 goto opengl_failed;
3654 // Update using texture info.
3655 scrtmp.Bpp = (2 << scrtmp.texture.u32);
3656 scrtmp.bpp = (scrtmp.Bpp * 8);
3657 scrtmp.buf.u32 = scrtmp.texture.buf.u32;
3658 scrtmp.width = scrtmp.texture.vis_width;
3659 scrtmp.height = scrtmp.texture.vis_height;
3660 scrtmp.pitch = (scrtmp.texture.vis_width <<
3661 (1 << scrtmp.texture.u32));
3663 scrtmp.is_opengl = scrtmp.want_opengl;
3664 DEBUG(("OpenGL screen configuration: is_opengl=%u buf.u32=%p pitch=%u",
3665 scrtmp.is_opengl, (void *)scrtmp.buf.u32, scrtmp.pitch));
3666 #endif
3667 // Screen is now initialized, update data.
3668 screen = scrtmp;
3669 #ifdef WITH_OPENGL
3670 if (!screen.is_opengl) {
3671 // Free OpenGL resources.
3672 DEBUG(("releasing OpenGL resources"));
3673 release_texture(screen.texture);
3675 #endif
3676 // Set up the Mega Drive screen.
3677 // Could not be done earlier because bpp was unknown.
3678 if ((mdscr.data == NULL) ||
3679 ((unsigned int)mdscr.bpp != screen.bpp) ||
3680 ((unsigned int)mdscr.w != (video.width + 16)) ||
3681 ((unsigned int)mdscr.h != (video.height + 16))) {
3682 mdscr.w = (video.width + 16);
3683 mdscr.h = (video.height + 16);
3684 mdscr.pitch = (mdscr.w * screen.Bpp);
3685 mdscr.bpp = screen.bpp;
3686 free(mdscr.data);
3687 mdscr.data = (uint8_t *)calloc(mdscr.h, mdscr.pitch);
3688 if (mdscr.data == NULL) {
3689 // Cannot recover. Clean up and bail out.
3690 memset(&mdscr, 0, sizeof(mdscr));
3691 return -2;
3693 mdscr_splash();
3695 DEBUG(("md screen configuration: w=%d h=%d bpp=%d pitch=%d data=%p",
3696 mdscr.w, mdscr.h, mdscr.bpp, mdscr.pitch, (void *)mdscr.data));
3697 // If we're in 8 bit mode, set color 0xff to white for the text,
3698 // and make a palette buffer.
3699 if (screen.bpp == 8) {
3700 SDL_Color color = { 0xff, 0xff, 0xff, 0x00 };
3702 SDL_SetColors(screen.surface, &color, 0xff, 1);
3703 memset(video.palette, 0x00, sizeof(video.palette));
3704 mdpal = video.palette;
3706 else
3707 mdpal = NULL;
3708 #ifdef WITH_THREADS
3709 if (screen.want_thread)
3710 screen_update_thread_start();
3711 #endif
3712 // Rehash filters.
3713 filters_stack_update();
3714 // Update screen.
3715 pd_graphics_update(true);
3716 return 0;
3720 * Set fullscreen mode.
3721 * @param toggle Nonzero to enable fullscreen, otherwise disable it.
3722 * @return 0 on success.
3724 static int set_fullscreen(int toggle)
3726 unsigned int w;
3727 unsigned int h;
3729 if (((!toggle) && (!screen.is_fullscreen)) ||
3730 ((toggle) && (screen.is_fullscreen))) {
3731 // Already in the desired mode.
3732 DEBUG(("already %s fullscreen mode, ret=-1",
3733 (toggle ? "in" : "not in")));
3734 return -1;
3736 #ifdef HAVE_SDL_WM_TOGGLEFULLSCREEN
3737 // Try this first.
3738 DEBUG(("trying SDL_WM_ToggleFullScreen(%p)", (void *)screen.surface));
3739 if (SDL_WM_ToggleFullScreen(screen.surface))
3740 return 0;
3741 DEBUG(("falling back to screen_init()"));
3742 #endif
3743 dgen_fullscreen = toggle;
3744 if (screen.surface != NULL) {
3745 // Try to keep the current mode.
3746 w = screen.surface->w;
3747 h = screen.surface->h;
3749 else if ((dgen_width > 0) && (dgen_height > 0)) {
3750 // Use configured mode.
3751 w = dgen_width;
3752 h = dgen_height;
3754 else {
3755 // Try to make a guess.
3756 w = (video.width * screen.x_scale);
3757 h = (video.height * screen.y_scale);
3759 DEBUG(("reinitializing screen with want_fullscreen=%u,"
3760 " screen_init(%u, %u)",
3761 screen.want_fullscreen, w, h));
3762 return screen_init(w, h);
3766 * Initialize SDL, and the graphics.
3767 * @param want_sound Nonzero if we want sound.
3768 * @param want_pal Nonzero for PAL mode.
3769 * @param hz Requested frame rate (between 0 and 1000).
3770 * @return Nonzero if successful.
3772 int pd_graphics_init(int want_sound, int want_pal, int hz)
3774 SDL_Event event;
3776 prompt_init(&prompt.status);
3777 if ((hz <= 0) || (hz > 1000)) {
3778 // You may as well disable bool_frameskip.
3779 fprintf(stderr, "sdl: invalid frame rate (%d)\n", hz);
3780 return 0;
3782 video.hz = hz;
3783 if (want_pal) {
3784 // PAL
3785 video.is_pal = 1;
3786 video.height = 240;
3788 else {
3789 // NTSC
3790 video.is_pal = 0;
3791 video.height = 224;
3793 #ifndef __MINGW32__
3794 // [fbcon workaround]
3795 // Disable SDL_FBACCEL (if unset) before calling SDL_Init() in case
3796 // fbcon is to be used. Prevents SDL_FillRect() and SDL_SetVideoMode()
3797 // from hanging when hardware acceleration is available.
3798 setenv("SDL_FBACCEL", "0", 0);
3799 // [fbcon workaround]
3800 // A mouse is never required.
3801 setenv("SDL_NOMOUSE", "1", 0);
3802 #endif
3803 if (SDL_Init(SDL_INIT_VIDEO | (want_sound ? SDL_INIT_AUDIO : 0))) {
3804 fprintf(stderr, "sdl: can't init SDL: %s\n", SDL_GetError());
3805 return 0;
3807 #ifndef __MINGW32__
3809 char buf[32];
3811 // [fbcon workaround]
3812 // Double buffering usually makes screen blink during refresh.
3813 if ((SDL_VideoDriverName(buf, sizeof(buf))) &&
3814 (!strcmp(buf, "fbcon")))
3815 dgen_doublebuffer = 0;
3817 #endif
3818 // Required for text input.
3819 SDL_EnableUNICODE(1);
3820 // Set the titlebar.
3821 SDL_WM_SetCaption("DGen/SDL " VER, "DGen/SDL " VER);
3822 // Hide the cursor.
3823 SDL_ShowCursor(0);
3824 // Initialize screen.
3825 if (screen_init(0, 0))
3826 goto fail;
3827 // Initialize scaling.
3828 set_scaling(scaling_names[dgen_scaling % NUM_SCALING]);
3829 DEBUG(("using scaling filter \"%s\"",
3830 scaling_names[dgen_scaling % NUM_SCALING]));
3831 DEBUG(("screen initialized"));
3832 #ifndef __MINGW32__
3833 // We don't need setuid privileges anymore
3834 if (getuid() != geteuid())
3835 setuid(getuid());
3836 DEBUG(("setuid privileges dropped"));
3837 #endif
3838 #ifdef WITH_CTV
3839 filters_pluck_ctv();
3841 const struct filter *f;
3843 f = filters_find(ctv_names[dgen_craptv % NUM_CTV]);
3844 if ((f != NULL) && (f->func != filter_off))
3845 filters_insert(f);
3847 #endif // WITH_CTV
3848 DEBUG(("ret=1"));
3849 fprintf(stderr, "video: %dx%d, %u bpp (%u Bpp), %uHz\n",
3850 screen.surface->w, screen.surface->h, screen.bpp,
3851 screen.Bpp, video.hz);
3852 #ifdef WITH_OPENGL
3853 if (screen.is_opengl) {
3854 DEBUG(("GL_VENDOR=\"%s\" GL_RENDERER=\"%s\""
3855 " GL_VERSION=\"%s\"",
3856 glGetString(GL_VENDOR), glGetString(GL_RENDERER),
3857 glGetString(GL_VERSION)));
3858 fprintf(stderr,
3859 "video: OpenGL texture %ux%ux%u (%ux%u)\n",
3860 screen.texture.width,
3861 screen.texture.height,
3862 (2 << screen.texture.u32),
3863 screen.texture.vis_width,
3864 screen.texture.vis_height);
3866 #endif
3867 while (SDL_PollEvent(&event)) {
3868 switch (event.type) {
3869 case SDL_VIDEORESIZE:
3870 if (screen_init(event.resize.w, event.resize.h))
3871 goto fail;
3872 break;
3875 return 1;
3876 fail:
3877 fprintf(stderr, "sdl: can't initialize graphics.\n");
3878 return 0;
3882 * Reinitialize graphics.
3883 * @param want_pal Nonzero for PAL mode.
3884 * @param hz Requested frame rate (between 0 and 1000).
3885 * @return Nonzero if successful.
3887 int pd_graphics_reinit(int, int want_pal, int hz)
3889 if ((hz <= 0) || (hz > 1000)) {
3890 // You may as well disable bool_frameskip.
3891 fprintf(stderr, "sdl: invalid frame rate (%d)\n", hz);
3892 return 0;
3894 video.hz = hz;
3895 if (want_pal) {
3896 // PAL
3897 video.is_pal = 1;
3898 video.height = 240;
3900 else {
3901 // NTSC
3902 video.is_pal = 0;
3903 video.height = 224;
3905 // Reinitialize screen.
3906 if (screen_init(screen.window_width, screen.window_height))
3907 goto fail;
3908 DEBUG(("screen reinitialized"));
3909 return 1;
3910 fail:
3911 fprintf(stderr, "sdl: can't reinitialize graphics.\n");
3912 return 0;
3916 * Update palette.
3918 void pd_graphics_palette_update()
3920 unsigned int i;
3922 for (i = 0; (i < 64); ++i) {
3923 screen.color[i].r = mdpal[(i << 2)];
3924 screen.color[i].g = mdpal[((i << 2) + 1)];
3925 screen.color[i].b = mdpal[((i << 2) + 2)];
3927 #ifdef WITH_OPENGL
3928 if (!screen.is_opengl)
3929 #endif
3930 SDL_SetColors(screen.surface, screen.color, 0, 64);
3934 * Display screen.
3935 * @param update False if screen buffer is garbage and must be updated first.
3937 void pd_graphics_update(bool update)
3939 static unsigned long fps_since = 0;
3940 static unsigned long frames_old = 0;
3941 static unsigned long frames = 0;
3942 unsigned long usecs = pd_usecs();
3943 const struct filter *f;
3944 struct filter_data *fd;
3945 size_t i;
3947 // Check whether the message must be processed.
3948 if ((events == STARTED) &&
3949 ((info.displayed) || (info.length)) &&
3950 ((usecs - info.since) >= MESSAGE_LIFE))
3951 pd_message_process();
3952 else if (dgen_fps) {
3953 unsigned long tmp = ((usecs - fps_since) & 0x3fffff);
3955 ++frames;
3956 if (tmp >= 1000000) {
3957 unsigned long fps;
3959 fps_since = usecs;
3960 if (frames_old > frames)
3961 fps = (frames_old - frames);
3962 else
3963 fps = (frames - frames_old);
3964 frames_old = frames;
3965 if (!info.displayed) {
3966 char buf[16];
3968 snprintf(buf, sizeof(buf), "%lu FPS", fps);
3969 pd_message_write(buf, strlen(buf), ~0u);
3973 if (update == false)
3974 mdscr_splash();
3975 // Process output through filters.
3976 for (i = 0; (i != elemof(filters_stack)); ++i) {
3977 f = filters_stack[i];
3978 fd = &filters_stack_data[i];
3979 if ((filters_stack_size == 0) ||
3980 (i == (filters_stack_size - 1)))
3981 break;
3982 f->func(fd, (fd + 1));
3984 // Lock screen.
3985 if (screen_lock())
3986 return;
3987 // Generate screen output with the last filter.
3988 f->func(fd, (fd + 1));
3989 // Unlock screen.
3990 screen_unlock();
3991 // Update the screen.
3992 screen_update();
3996 * Callback for sound.
3997 * @param stream Sound destination buffer.
3998 * @param len Length of destination buffer.
4000 static void snd_callback(void *, Uint8 *stream, int len)
4002 size_t wrote;
4004 // Slurp off the play buffer
4005 wrote = cbuf_read(stream, &sound.cbuf, len);
4006 if (wrote == (size_t)len)
4007 return;
4008 // Not enough data, fill remaining space with silence.
4009 memset(&stream[wrote], 0, ((size_t)len - wrote));
4013 * Initialize the sound.
4014 * @param freq Sound samples rate.
4015 * @param[in,out] samples Minimum buffer size in samples.
4016 * @return Nonzero on success.
4018 int pd_sound_init(long &freq, unsigned int &samples)
4020 SDL_AudioSpec wanted;
4021 SDL_AudioSpec spec;
4023 // Clean up first.
4024 pd_sound_deinit();
4026 // Set the desired format
4027 wanted.freq = freq;
4028 #ifdef WORDS_BIGENDIAN
4029 wanted.format = AUDIO_S16MSB;
4030 #else
4031 wanted.format = AUDIO_S16LSB;
4032 #endif
4033 wanted.channels = 2;
4034 wanted.samples = dgen_soundsamples;
4035 wanted.callback = snd_callback;
4036 wanted.userdata = NULL;
4038 if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
4039 fprintf(stderr, "sdl: unable to initialize audio\n");
4040 return 0;
4043 // Open audio, and get the real spec
4044 if (SDL_OpenAudio(&wanted, &spec) < 0) {
4045 fprintf(stderr,
4046 "sdl: couldn't open audio: %s\n",
4047 SDL_GetError());
4048 return 0;
4051 // Check everything
4052 if (spec.channels != 2) {
4053 fprintf(stderr, "sdl: couldn't get stereo audio format.\n");
4054 goto snd_error;
4056 if (spec.format != wanted.format) {
4057 fprintf(stderr, "sdl: unable to get 16-bit audio.\n");
4058 goto snd_error;
4061 // Set things as they really are
4062 sound.rate = freq = spec.freq;
4063 sndi.len = (spec.freq / video.hz);
4064 sound.samples = spec.samples;
4065 samples += sound.samples;
4067 // Calculate buffer size (sample size = (channels * (bits / 8))).
4068 sound.cbuf.size = (samples * (2 * (16 / 8)));
4069 sound.cbuf.i = 0;
4070 sound.cbuf.s = 0;
4072 fprintf(stderr, "sound: %uHz, %d samples, buffer: %u bytes\n",
4073 sound.rate, spec.samples, (unsigned int)sound.cbuf.size);
4075 // Allocate zero-filled play buffer.
4076 sndi.lr = (int16_t *)calloc(2, (sndi.len * sizeof(sndi.lr[0])));
4078 sound.cbuf.data.i16 = (int16_t *)calloc(1, sound.cbuf.size);
4079 if ((sndi.lr == NULL) || (sound.cbuf.data.i16 == NULL)) {
4080 fprintf(stderr, "sdl: couldn't allocate sound buffers.\n");
4081 goto snd_error;
4084 // Start sound output.
4085 SDL_PauseAudio(0);
4087 // It's all good!
4088 return 1;
4090 snd_error:
4091 // Oops! Something bad happened, cleanup.
4092 SDL_CloseAudio();
4093 free((void *)sndi.lr);
4094 sndi.lr = NULL;
4095 sndi.len = 0;
4096 free((void *)sound.cbuf.data.i16);
4097 sound.cbuf.data.i16 = NULL;
4098 memset(&sound, 0, sizeof(sound));
4099 return 0;
4103 * Deinitialize sound subsystem.
4105 void pd_sound_deinit()
4107 if (sound.cbuf.data.i16 != NULL) {
4108 SDL_PauseAudio(1);
4109 SDL_CloseAudio();
4110 free((void *)sound.cbuf.data.i16);
4112 memset(&sound, 0, sizeof(sound));
4113 free((void*)sndi.lr);
4114 sndi.lr = NULL;
4118 * Return samples read/write indices in the buffer.
4120 unsigned int pd_sound_rp()
4122 unsigned int ret;
4124 if (!sound.cbuf.size)
4125 return 0;
4126 SDL_LockAudio();
4127 ret = sound.cbuf.i;
4128 SDL_UnlockAudio();
4129 return (ret >> 2);
4132 unsigned int pd_sound_wp()
4134 unsigned int ret;
4136 if (!sound.cbuf.size)
4137 return 0;
4138 SDL_LockAudio();
4139 ret = ((sound.cbuf.i + sound.cbuf.s) % sound.cbuf.size);
4140 SDL_UnlockAudio();
4141 return (ret >> 2);
4145 * Write contents of sndi to sound.cbuf.
4147 void pd_sound_write()
4149 if (!sound.cbuf.size)
4150 return;
4151 SDL_LockAudio();
4152 cbuf_write(&sound.cbuf, (uint8_t *)sndi.lr, (sndi.len * 4));
4153 SDL_UnlockAudio();
4157 * Tells whether DGen stopped intentionally so emulation can resume without
4158 * skipping frames.
4160 int pd_stopped()
4162 int ret = stopped;
4164 stopped = 0;
4165 return ret;
4169 * Keyboard input.
4171 typedef struct {
4172 char *buf;
4173 size_t pos;
4174 size_t size;
4175 } kb_input_t;
4178 * Keyboard input results.
4180 enum kb_input {
4181 KB_INPUT_ABORTED,
4182 KB_INPUT_ENTERED,
4183 KB_INPUT_CONSUMED,
4184 KB_INPUT_IGNORED
4188 * Manage text input with some rudimentary history.
4189 * @param input Input buffer.
4190 * @param ksym Keyboard symbol.
4191 * @param ksym_uni Unicode translation for keyboard symbol.
4192 * @return Input result.
4194 static enum kb_input kb_input(kb_input_t *input, uint32_t ksym,
4195 uint16_t ksym_uni)
4197 #define HISTORY_LEN 32
4198 static char history[HISTORY_LEN][64];
4199 static int history_pos = -1;
4200 static int history_len = 0;
4201 char c;
4203 if (ksym & KEYSYM_MOD_CTRL)
4204 return KB_INPUT_IGNORED;
4205 if (isprint((c = ksym_uni))) {
4206 if (input->pos >= (input->size - 1))
4207 return KB_INPUT_CONSUMED;
4208 if (input->buf[input->pos] == '\0')
4209 input->buf[(input->pos + 1)] = '\0';
4210 input->buf[input->pos] = c;
4211 ++input->pos;
4212 return KB_INPUT_CONSUMED;
4214 else if (ksym == SDLK_DELETE) {
4215 size_t tail;
4217 if (input->buf[input->pos] == '\0')
4218 return KB_INPUT_CONSUMED;
4219 tail = ((input->size - input->pos) + 1);
4220 memmove(&input->buf[input->pos],
4221 &input->buf[(input->pos + 1)],
4222 tail);
4223 return KB_INPUT_CONSUMED;
4225 else if (ksym == SDLK_BACKSPACE) {
4226 size_t tail;
4228 if (input->pos == 0)
4229 return KB_INPUT_CONSUMED;
4230 --input->pos;
4231 tail = ((input->size - input->pos) + 1);
4232 memmove(&input->buf[input->pos],
4233 &input->buf[(input->pos + 1)],
4234 tail);
4235 return KB_INPUT_CONSUMED;
4237 else if (ksym == SDLK_LEFT) {
4238 if (input->pos != 0)
4239 --input->pos;
4240 return KB_INPUT_CONSUMED;
4242 else if (ksym == SDLK_RIGHT) {
4243 if (input->buf[input->pos] != '\0')
4244 ++input->pos;
4245 return KB_INPUT_CONSUMED;
4247 else if ((ksym == SDLK_RETURN) || (ksym == SDLK_KP_ENTER)) {
4248 history_pos = -1;
4249 if (input->pos == 0)
4250 return KB_INPUT_ABORTED;
4251 if (history_len < HISTORY_LEN)
4252 ++history_len;
4253 memmove(&history[1], &history[0],
4254 ((history_len - 1) * sizeof(history[0])));
4255 strncpy(history[0], input->buf, sizeof(history[0]));
4256 return KB_INPUT_ENTERED;
4258 else if (ksym == SDLK_ESCAPE) {
4259 history_pos = 0;
4260 return KB_INPUT_ABORTED;
4262 else if (ksym == SDLK_UP) {
4263 if (input->size == 0)
4264 return KB_INPUT_CONSUMED;
4265 if (history_pos < (history_len - 1))
4266 ++history_pos;
4267 strncpy(input->buf, history[history_pos], input->size);
4268 input->buf[(input->size - 1)] = '\0';
4269 input->pos = strlen(input->buf);
4270 return KB_INPUT_CONSUMED;
4272 else if (ksym == SDLK_DOWN) {
4273 if ((input->size == 0) || (history_pos < 0))
4274 return KB_INPUT_CONSUMED;
4275 if (history_pos > 0)
4276 --history_pos;
4277 strncpy(input->buf, history[history_pos], input->size);
4278 input->buf[(input->size - 1)] = '\0';
4279 input->pos = strlen(input->buf);
4280 return KB_INPUT_CONSUMED;
4282 return KB_INPUT_IGNORED;
4286 * Write a message to the status bar while displaying a cursor and without
4287 * buffering.
4289 static void pd_message_cursor(unsigned int mark, const char *msg, ...)
4291 va_list vl;
4292 char buf[1024];
4293 size_t len;
4294 size_t disp_len;
4296 va_start(vl, msg);
4297 len = (size_t)vsnprintf(buf, sizeof(buf), msg, vl);
4298 va_end(vl);
4299 buf[(sizeof(buf) - 1)] = '\0';
4300 disp_len = font_text_max_len(screen.width, screen.info_height,
4301 FONT_TYPE_AUTO);
4302 if (mark > len) {
4303 if (len <= disp_len)
4304 pd_message_display(buf, len, ~0u, true);
4305 else
4306 pd_message_display(&(buf[(len - disp_len)]), disp_len,
4307 ~0u, true);
4308 return;
4310 if (len <= disp_len)
4311 pd_message_display(buf, len, mark, true);
4312 else if (len == mark)
4313 pd_message_display(&buf[((len - disp_len) + 1)],
4314 disp_len, (disp_len - 1), true);
4315 else if ((len - mark) < disp_len)
4316 pd_message_display(&buf[(len - disp_len)], disp_len,
4317 (mark - (len - disp_len)), true);
4318 else if (mark != ~0u)
4319 pd_message_display(&buf[mark], disp_len, 0, true);
4323 * Rehash rc vars that require special handling (see "SH" in rc.cpp).
4325 static int prompt_rehash_rc_field(const struct rc_field *rc, md& megad)
4327 bool fail = false;
4328 bool init_video = false;
4329 bool init_sound = false;
4330 bool init_joystick = false;
4332 if (rc->variable == &dgen_craptv) {
4333 #ifdef WITH_CTV
4334 filters_pluck_ctv();
4335 filters_insert(filters_find(ctv_names[dgen_craptv % NUM_CTV]));
4336 #else
4337 fail = true;
4338 #endif
4340 else if (rc->variable == &dgen_scaling) {
4341 if (set_scaling(scaling_names[dgen_scaling]) == 0)
4342 fail = true;
4344 else if (rc->variable == &dgen_emu_z80) {
4345 megad.z80_state_dump();
4346 // Z80: 0 = none, 1 = CZ80, 2 = MZ80, 3 = DRZ80
4347 switch (dgen_emu_z80) {
4348 #ifdef WITH_MZ80
4349 case 1:
4350 megad.z80_core = md::Z80_CORE_MZ80;
4351 break;
4352 #endif
4353 #ifdef WITH_CZ80
4354 case 2:
4355 megad.z80_core = md::Z80_CORE_CZ80;
4356 break;
4357 #endif
4358 #ifdef WITH_DRZ80
4359 case 3:
4360 megad.z80_core = md::Z80_CORE_DRZ80;
4361 break;
4362 #endif
4363 default:
4364 megad.z80_core = md::Z80_CORE_NONE;
4365 break;
4367 megad.z80_state_restore();
4369 else if (rc->variable == &dgen_emu_m68k) {
4370 megad.m68k_state_dump();
4371 // M68K: 0 = none, 1 = StarScream, 2 = Musashi, 3 = Cyclone
4372 switch (dgen_emu_m68k) {
4373 #ifdef WITH_STAR
4374 case 1:
4375 megad.cpu_emu = md::CPU_EMU_STAR;
4376 break;
4377 #endif
4378 #ifdef WITH_MUSA
4379 case 2:
4380 megad.cpu_emu = md::CPU_EMU_MUSA;
4381 break;
4382 #endif
4383 #ifdef WITH_CYCLONE
4384 case 3:
4385 megad.cpu_emu = md::CPU_EMU_CYCLONE;
4386 break;
4387 #endif
4388 default:
4389 megad.cpu_emu = md::CPU_EMU_NONE;
4390 break;
4392 megad.m68k_state_restore();
4394 else if ((rc->variable == &dgen_sound) ||
4395 (rc->variable == &dgen_soundrate) ||
4396 (rc->variable == &dgen_soundsegs) ||
4397 (rc->variable == &dgen_soundsamples) ||
4398 (rc->variable == &dgen_mjazz))
4399 init_sound = true;
4400 else if (rc->variable == &dgen_fullscreen) {
4401 if (screen.want_fullscreen != (!!dgen_fullscreen)) {
4402 init_video = true;
4405 else if ((rc->variable == &dgen_info_height) ||
4406 (rc->variable == &dgen_width) ||
4407 (rc->variable == &dgen_height) ||
4408 (rc->variable == &dgen_x_scale) ||
4409 (rc->variable == &dgen_y_scale) ||
4410 (rc->variable == &dgen_depth) ||
4411 (rc->variable == &dgen_doublebuffer) ||
4412 (rc->variable == &dgen_screen_thread))
4413 init_video = true;
4414 else if (rc->variable == &dgen_swab) {
4415 #ifdef WITH_CTV
4416 set_swab();
4417 #else
4418 fail = true;
4419 #endif
4421 else if ((rc->variable == &dgen_scale) ||
4422 (rc->variable == &dgen_aspect)) {
4423 dgen_x_scale = dgen_scale;
4424 dgen_y_scale = dgen_scale;
4425 init_video = true;
4427 else if ((rc->variable == &dgen_opengl) ||
4428 (rc->variable == &dgen_opengl_stretch) ||
4429 (rc->variable == &dgen_opengl_linear) ||
4430 (rc->variable == &dgen_opengl_32bit) ||
4431 (rc->variable == &dgen_opengl_square)) {
4432 #ifdef WITH_OPENGL
4433 init_video = true;
4434 #else
4435 (void)0;
4436 #endif
4438 else if (rc->variable == &dgen_joystick)
4439 init_joystick = true;
4440 else if (rc->variable == &dgen_hz) {
4441 // See md::md().
4442 if (dgen_hz <= 0)
4443 dgen_hz = 1;
4444 else if (dgen_hz > 1000)
4445 dgen_hz = 1000;
4446 if (((unsigned int)dgen_hz != video.hz) ||
4447 ((unsigned int)dgen_hz != megad.vhz)) {
4448 video.hz = dgen_hz;
4449 init_video = true;
4450 init_sound = true;
4453 else if (rc->variable == &dgen_pal) {
4454 // See md::md().
4455 if ((dgen_pal) &&
4456 ((video.is_pal == false) ||
4457 (megad.pal == 0) ||
4458 (video.height != PAL_VBLANK))) {
4459 megad.pal = 1;
4460 megad.init_pal();
4461 video.is_pal = true;
4462 video.height = PAL_VBLANK;
4463 init_video = true;
4465 else if ((!dgen_pal) &&
4466 ((video.is_pal == true) ||
4467 (megad.pal == 1) ||
4468 (video.height != NTSC_VBLANK))) {
4469 megad.pal = 0;
4470 megad.init_pal();
4471 video.is_pal = false;
4472 video.height = NTSC_VBLANK;
4473 init_video = true;
4476 else if (rc->variable == &dgen_region) {
4477 uint8_t c;
4478 int hz;
4479 int pal;
4480 int vblank;
4482 if (dgen_region)
4483 c = dgen_region;
4484 else
4485 c = megad.region_guess();
4486 md::region_info(c, &pal, &hz, &vblank, 0, 0);
4487 if ((hz != dgen_hz) || (pal != dgen_pal) ||
4488 (c != megad.region)) {
4489 megad.region = c;
4490 dgen_hz = hz;
4491 dgen_pal = pal;
4492 megad.pal = pal;
4493 megad.init_pal();
4494 video.is_pal = pal;
4495 video.height = vblank;
4496 video.hz = hz;
4497 init_video = true;
4498 init_sound = true;
4499 fprintf(stderr,
4500 "sdl: reconfiguring for region \"%c\": "
4501 "%dHz (%s)\n", megad.region, hz,
4502 (pal ? "PAL" : "NTSC"));
4505 else if (rc->variable == (intptr_t *)((void *)&dgen_rom_path))
4506 set_rom_path(dgen_rom_path.val);
4507 if (init_video) {
4508 // This is essentially what pd_graphics_init() does.
4509 memset(megad.vdp.dirt, 0xff, 0x35);
4510 switch (screen_init(screen.window_width,
4511 screen.window_height)) {
4512 case 0:
4513 break;
4514 case -1:
4515 goto video_warn;
4516 default:
4517 goto video_fail;
4520 if (init_sound) {
4521 if (video.hz == 0)
4522 fail = true;
4523 else if (dgen_sound == 0)
4524 pd_sound_deinit();
4525 else {
4526 uint8_t ym2612_buf[512];
4527 uint8_t sn76496_buf[16];
4528 unsigned int samples;
4529 long rate = dgen_soundrate;
4531 pd_sound_deinit();
4532 samples = (dgen_soundsegs * (rate / video.hz));
4533 if (!pd_sound_init(rate, samples))
4534 fail = true;
4535 YM2612_dump(0, ym2612_buf);
4536 SN76496_dump(0, sn76496_buf);
4537 megad.init_sound();
4538 SN76496_restore(0, sn76496_buf);
4539 YM2612_restore(0, ym2612_buf);
4542 if (init_joystick) {
4543 #ifdef WITH_JOYSTICK
4544 megad.deinit_joysticks();
4545 if (dgen_joystick)
4546 megad.init_joysticks();
4547 #else
4548 fail = true;
4549 #endif
4551 if (fail) {
4552 pd_message("Failed to rehash value.");
4553 return (PROMPT_RET_EXIT | PROMPT_RET_MSG);
4555 return PROMPT_RET_CONT;
4556 video_warn:
4557 pd_message("Failed to reinitialize video.");
4558 return (PROMPT_RET_EXIT | PROMPT_RET_MSG);
4559 video_fail:
4560 fprintf(stderr, "sdl: fatal error while trying to change screen"
4561 " resolution.\n");
4562 return (PROMPT_RET_ERROR | PROMPT_RET_MSG);
4565 static void prompt_show_rc_field(const struct rc_field *rc)
4567 size_t i;
4568 intptr_t val = *rc->variable;
4570 if ((rc->parser == rc_number) ||
4571 (rc->parser == rc_soundrate))
4572 pd_message("%s is %ld", rc->fieldname, val);
4573 else if (rc->parser == rc_keysym) {
4574 char *ks = dump_keysym(val);
4576 if ((ks == NULL) || (ks[0] == '\0'))
4577 pd_message("%s isn't bound", rc->fieldname);
4578 else
4579 pd_message("%s is bound to \"%s\"", rc->fieldname, ks);
4580 free(ks);
4582 else if (rc->parser == rc_boolean)
4583 pd_message("%s is %s", rc->fieldname,
4584 ((val) ? "true" : "false"));
4585 else if (rc->parser == rc_joypad) {
4586 char *js = dump_joypad(val);
4588 if ((js == NULL) || (js[0] == '\0'))
4589 pd_message("%s isn't bound", rc->fieldname);
4590 else
4591 pd_message("%s is bound to \"%s\"", rc->fieldname, js);
4592 free(js);
4594 else if (rc->parser == rc_mouse) {
4595 char *mo = dump_mouse(val);
4597 if ((mo == NULL) || (mo[0] == '\0'))
4598 pd_message("%s isn't bound", rc->fieldname);
4599 else
4600 pd_message("%s is bound to \"%s\"", rc->fieldname, mo);
4601 free(mo);
4603 else if (rc->parser == rc_ctv) {
4604 i = val;
4605 if (i >= NUM_CTV)
4606 pd_message("%s is undefined", rc->fieldname);
4607 else
4608 pd_message("%s is \"%s\"", rc->fieldname,
4609 ctv_names[i]);
4611 else if (rc->parser == rc_scaling) {
4612 i = val;
4613 if (i >= NUM_SCALING)
4614 pd_message("%s is undefined", rc->fieldname);
4615 else
4616 pd_message("%s is \"%s\"", rc->fieldname,
4617 scaling_names[i]);
4619 else if (rc->parser == rc_emu_z80) {
4620 for (i = 0; (emu_z80_names[i] != NULL); ++i)
4621 if (i == (size_t)val)
4622 break;
4623 if (emu_z80_names[i] == NULL)
4624 pd_message("%s is undefined", rc->fieldname);
4625 else
4626 pd_message("%s is \"%s\"", rc->fieldname,
4627 emu_z80_names[i]);
4629 else if (rc->parser == rc_emu_m68k) {
4630 for (i = 0; (emu_m68k_names[i] != NULL); ++i)
4631 if (i == (size_t)val)
4632 break;
4633 if (emu_m68k_names[i] == NULL)
4634 pd_message("%s is undefined", rc->fieldname);
4635 else
4636 pd_message("%s is \"%s\"", rc->fieldname,
4637 emu_m68k_names[i]);
4639 else if (rc->parser == rc_region) {
4640 const char *s;
4642 if (val == 'U')
4643 s = "America (NTSC)";
4644 else if (val == 'E')
4645 s = "Europe (PAL)";
4646 else if (val == 'J')
4647 s = "Japan (NTSC)";
4648 else if (val == 'X')
4649 s = "Japan (PAL)";
4650 else
4651 s = "Auto";
4652 pd_message("%s is \"%c\" (%s)", rc->fieldname,
4653 (val ? (char)val : (char)' '), s);
4655 else if ((rc->parser == rc_string) ||
4656 (rc->parser == rc_rom_path)) {
4657 struct rc_str *rs = (struct rc_str *)rc->variable;
4658 char *s;
4660 if (rs->val == NULL)
4661 pd_message("%s has no value", rc->fieldname);
4662 else if ((s = backslashify((const uint8_t *)rs->val,
4663 strlen(rs->val), 0,
4664 NULL)) != NULL) {
4665 pd_message("%s is \"%s\"", rc->fieldname, s);
4666 free(s);
4668 else
4669 pd_message("%s can't be displayed", rc->fieldname);
4671 else if (rc->parser == rc_bind) {
4672 char *f = backslashify((uint8_t *)rc->fieldname,
4673 strlen(rc->fieldname), 0, NULL);
4674 char *s = *(char **)rc->variable;
4676 assert(s != NULL);
4677 assert((intptr_t)s != -1);
4678 s = backslashify((uint8_t *)s, strlen(s), 0, NULL);
4679 if ((f == NULL) || (s == NULL))
4680 pd_message("%s can't be displayed", rc->fieldname);
4681 else
4682 pd_message("%s is bound to \"%s\"", f, s);
4683 free(f);
4684 free(s);
4686 else
4687 pd_message("%s: can't display value", rc->fieldname);
4690 static int handle_prompt_enter(class md& md)
4692 struct prompt_parse pp;
4693 struct prompt *p = &prompt.status;
4694 size_t i;
4695 int ret;
4696 bool binding_tried = false;
4698 if (prompt_parse(p, &pp) == NULL)
4699 return PROMPT_RET_ERROR;
4700 if (pp.argc == 0) {
4701 ret = PROMPT_RET_EXIT;
4702 goto end;
4704 ret = 0;
4705 // Look for a command with that name.
4706 for (i = 0; (prompt_command[i].name != NULL); ++i) {
4707 int cret;
4709 if (strcasecmp(prompt_command[i].name, (char *)pp.argv[0]))
4710 continue;
4711 cret = prompt_command[i].cmd(md, pp.argc,
4712 (const char **)pp.argv);
4713 if ((cret & ~CMD_MSG) == CMD_ERROR)
4714 ret |= PROMPT_RET_ERROR;
4715 if (cret & CMD_MSG)
4716 ret |= PROMPT_RET_MSG;
4717 else if (cret & CMD_FAIL) {
4718 pd_message("%s: command failed", (char *)pp.argv[0]);
4719 ret |= PROMPT_RET_MSG;
4721 else if (cret & CMD_EINVAL) {
4722 pd_message("%s: invalid argument", (char *)pp.argv[0]);
4723 ret |= PROMPT_RET_MSG;
4725 goto end;
4727 binding_retry:
4728 // Look for a variable with that name.
4729 for (i = 0; (rc_fields[i].fieldname != NULL); ++i) {
4730 intptr_t potential;
4732 if (strcasecmp(rc_fields[i].fieldname, (char *)pp.argv[0]))
4733 continue;
4734 // Display current value?
4735 if (pp.argv[1] == NULL) {
4736 prompt_show_rc_field(&rc_fields[i]);
4737 ret |= PROMPT_RET_MSG;
4738 break;
4740 // Parse and set value.
4741 potential = rc_fields[i].parser((char *)pp.argv[1],
4742 rc_fields[i].variable);
4743 if ((rc_fields[i].parser != rc_number) && (potential == -1)) {
4744 pd_message("%s: invalid value", (char *)pp.argv[0]);
4745 ret |= PROMPT_RET_MSG;
4746 break;
4748 if ((rc_fields[i].parser == rc_string) ||
4749 (rc_fields[i].parser == rc_rom_path)) {
4750 struct rc_str *rs;
4752 rs = (struct rc_str *)rc_fields[i].variable;
4753 if (rc_str_list == NULL) {
4754 atexit(rc_str_cleanup);
4755 rc_str_list = rs;
4757 else if (rs->alloc == NULL) {
4758 rs->next = rc_str_list;
4759 rc_str_list = rs;
4761 else
4762 free(rs->alloc);
4763 rs->alloc = (char *)potential;
4764 rs->val = rs->alloc;
4766 else
4767 *(rc_fields[i].variable) = potential;
4768 ret |= prompt_rehash_rc_field(&rc_fields[i], md);
4769 break;
4771 if (rc_fields[i].fieldname == NULL) {
4772 if ((binding_tried == false) &&
4773 (rc_binding_add((char *)pp.argv[0], "") != NULL)) {
4774 binding_tried = true;
4775 goto binding_retry;
4777 pd_message("%s: unknown command", (char *)pp.argv[0]);
4778 ret |= PROMPT_RET_MSG;
4780 end:
4781 prompt_parse_clean(&pp);
4782 prompt_push(p);
4783 ret |= PROMPT_RET_ENTER;
4784 return ret;
4787 static void handle_prompt_complete_clear()
4789 complete_path_free(prompt.complete);
4790 prompt.complete = NULL;
4791 prompt.skip = 0;
4792 prompt.common = 0;
4795 static int handle_prompt_complete(class md& md, bool rwd)
4797 struct prompt_parse pp;
4798 struct prompt *p = &prompt.status;
4799 size_t prompt_common = 0; // escaped version of prompt.common
4800 unsigned int skip;
4801 size_t i;
4802 const char *arg;
4803 unsigned int alen;
4804 char *s = NULL;
4806 if (prompt_parse(p, &pp) == NULL)
4807 return PROMPT_RET_ERROR;
4808 if (rwd)
4809 prompt.skip -= 2;
4810 if (pp.index == 0) {
4811 const char *cs = NULL;
4812 const char *cm = NULL;
4813 size_t common;
4814 unsigned int tmp;
4816 assert(prompt.complete == NULL);
4817 // The first argument needs to be completed. This is either
4818 // a command or a variable name.
4819 arg = (const char *)pp.argv[0];
4820 alen = pp.cursor;
4821 if ((arg == NULL) || (alen == ~0u)) {
4822 arg = "";
4823 alen = 0;
4825 common = ~0u;
4826 complete_cmd_var:
4827 skip = prompt.skip;
4828 for (i = 0; (prompt_command[i].name != NULL); ++i) {
4829 if (strncasecmp(prompt_command[i].name, arg, alen))
4830 continue;
4831 if (cm == NULL)
4832 tmp = strlen(prompt_command[i].name);
4833 else
4834 tmp = strcommon(prompt_command[i].name, cm);
4835 cm = prompt_command[i].name;
4836 if (tmp < common)
4837 common = tmp;
4838 if (skip != 0) {
4839 --skip;
4840 continue;
4842 if (cs == NULL)
4843 cs = prompt_command[i].name;
4844 if (common == 0)
4845 goto complete_cmd_found;
4847 // Variables.
4848 for (i = 0; (rc_fields[i].fieldname != NULL); ++i) {
4849 if (strncasecmp(rc_fields[i].fieldname, arg, alen))
4850 continue;
4851 if (cm == NULL)
4852 tmp = strlen(rc_fields[i].fieldname);
4853 else
4854 tmp = strcommon(rc_fields[i].fieldname, cm);
4855 cm = rc_fields[i].fieldname;
4856 if (tmp < common)
4857 common = tmp;
4858 if (skip != 0) {
4859 --skip;
4860 continue;
4862 if (cs == NULL)
4863 cs = rc_fields[i].fieldname;
4864 if (common == 0)
4865 break;
4867 if (cs == NULL) {
4868 // Nothing matched, try again if possible.
4869 if (prompt.skip) {
4870 prompt.skip = 0;
4871 goto complete_cmd_var;
4873 goto end;
4875 complete_cmd_found:
4876 ++prompt.skip;
4877 s = backslashify((const uint8_t *)cs, strlen(cs), 0, &common);
4878 if (s == NULL)
4879 goto end;
4880 if (common != ~0u) {
4881 prompt.common = common;
4882 prompt_common = common;
4884 goto replace;
4886 // Complete function arguments.
4887 for (i = 0; (prompt_command[i].name != NULL); ++i) {
4888 char *t;
4890 if (strcasecmp(prompt_command[i].name,
4891 (const char *)pp.argv[0]))
4892 continue;
4893 if (prompt_command[i].cmpl == NULL)
4894 goto end;
4895 t = prompt_command[i].cmpl(md, pp.argc, (const char **)pp.argv,
4896 pp.cursor);
4897 if (t == NULL)
4898 goto end;
4899 prompt_common = prompt.common;
4900 s = backslashify((const uint8_t *)t, strlen(t), 0,
4901 &prompt_common);
4902 free(t);
4903 if (s == NULL)
4904 goto end;
4905 goto replace;
4907 // Variable value completion.
4908 arg = (const char *)pp.argv[pp.index];
4909 alen = pp.cursor;
4910 if ((arg == NULL) || (alen == ~0u)) {
4911 arg = "";
4912 alen = 0;
4914 for (i = 0; (rc_fields[i].fieldname != NULL); ++i) {
4915 struct rc_field *rc = &rc_fields[i];
4916 const char **names;
4918 if (strcasecmp(rc->fieldname, (const char *)pp.argv[0]))
4919 continue;
4920 // Boolean values.
4921 if (rc->parser == rc_boolean)
4922 s = strdup((prompt.skip & 1) ? "true" : "false");
4923 // ROM path.
4924 else if (rc->parser == rc_rom_path) {
4925 if (prompt.complete == NULL) {
4926 prompt.complete =
4927 complete_path(arg, alen, NULL);
4928 prompt.skip = 0;
4929 rehash_prompt_complete_common();
4931 if (prompt.complete != NULL) {
4932 char **ret = prompt.complete;
4934 rc_rom_path_retry:
4935 skip = prompt.skip;
4936 for (i = 0; (ret[i] != NULL); ++i) {
4937 if (skip == 0)
4938 break;
4939 --skip;
4941 if (ret[i] == NULL) {
4942 if (prompt.skip != 0) {
4943 prompt.skip = 0;
4944 goto rc_rom_path_retry;
4947 else {
4948 prompt_common = prompt.common;
4949 s = backslashify
4950 ((const uint8_t *)ret[i],
4951 strlen(ret[i]), 0,
4952 &prompt_common);
4956 // Numbers.
4957 else if ((rc->parser == rc_number) ||
4958 (rc->parser == rc_soundrate)) {
4959 char buf[10];
4961 rc_number_retry:
4962 if (snprintf(buf, sizeof(buf), "%d",
4963 (int)prompt.skip) >= (int)sizeof(buf)) {
4964 prompt.skip = 0;
4965 goto rc_number_retry;
4967 s = strdup(buf);
4969 // CTV filters, scaling algorithms, Z80, M68K.
4970 else if ((names = ctv_names, rc->parser == rc_ctv) ||
4971 (names = scaling_names, rc->parser == rc_scaling) ||
4972 (names = emu_z80_names, rc->parser == rc_emu_z80) ||
4973 (names = emu_m68k_names, rc->parser == rc_emu_m68k)) {
4974 rc_names_retry:
4975 skip = prompt.skip;
4976 for (i = 0; (names[i] != NULL); ++i) {
4977 if (skip == 0)
4978 break;
4979 --skip;
4981 if (names[i] == NULL) {
4982 if (prompt.skip != 0) {
4983 prompt.skip = 0;
4984 goto rc_names_retry;
4987 else
4988 s = strdup(names[i]);
4990 if (s == NULL)
4991 break;
4992 ++prompt.skip;
4993 goto replace;
4995 goto end;
4996 replace:
4997 prompt_replace(p, pp.argo[pp.index].pos, pp.argo[pp.index].len,
4998 (const uint8_t *)s, strlen(s));
4999 if (prompt_common) {
5000 unsigned int cursor;
5002 cursor = (pp.argo[pp.index].pos + prompt_common);
5003 if (cursor > p->history[(p->current)].length)
5004 cursor = p->history[(p->current)].length;
5005 if (cursor != p->cursor) {
5006 p->cursor = cursor;
5007 handle_prompt_complete_clear();
5010 end:
5011 free(s);
5012 prompt_parse_clean(&pp);
5013 return 0;
5016 static int handle_prompt(uint32_t ksym, uint16_t ksym_uni, md& megad)
5018 struct prompt::prompt_history *ph;
5019 size_t sz;
5020 uint8_t c[6];
5021 char *s;
5022 int ret = PROMPT_RET_CONT;
5023 struct prompt *p = &prompt.status;
5024 struct prompt_parse pp;
5026 if (ksym == 0)
5027 goto end;
5028 switch (ksym & ~KEYSYM_MOD_MASK) {
5029 case SDLK_UP:
5030 handle_prompt_complete_clear();
5031 prompt_older(p);
5032 break;
5033 case SDLK_DOWN:
5034 handle_prompt_complete_clear();
5035 prompt_newer(p);
5036 break;
5037 case SDLK_LEFT:
5038 handle_prompt_complete_clear();
5039 prompt_left(p);
5040 break;
5041 case SDLK_RIGHT:
5042 handle_prompt_complete_clear();
5043 prompt_right(p);
5044 break;
5045 case SDLK_HOME:
5046 handle_prompt_complete_clear();
5047 prompt_begin(p);
5048 break;
5049 case SDLK_END:
5050 handle_prompt_complete_clear();
5051 prompt_end(p);
5052 break;
5053 case SDLK_BACKSPACE:
5054 handle_prompt_complete_clear();
5055 prompt_backspace(p);
5056 break;
5057 case SDLK_DELETE:
5058 handle_prompt_complete_clear();
5059 prompt_delete(p);
5060 break;
5061 case SDLK_a:
5062 // ^A
5063 if ((ksym & KEYSYM_MOD_CTRL) == 0)
5064 goto other;
5065 handle_prompt_complete_clear();
5066 prompt_begin(p);
5067 break;
5068 case SDLK_e:
5069 // ^E
5070 if ((ksym & KEYSYM_MOD_CTRL) == 0)
5071 goto other;
5072 handle_prompt_complete_clear();
5073 prompt_end(p);
5074 break;
5075 case SDLK_u:
5076 // ^U
5077 if ((ksym & KEYSYM_MOD_CTRL) == 0)
5078 goto other;
5079 handle_prompt_complete_clear();
5080 prompt_clear(p);
5081 break;
5082 case SDLK_k:
5083 // ^K
5084 if ((ksym & KEYSYM_MOD_CTRL) == 0)
5085 goto other;
5086 handle_prompt_complete_clear();
5087 prompt_replace(p, p->cursor, ~0u, NULL, 0);
5088 break;
5089 case SDLK_w:
5090 // ^W
5091 if ((ksym & KEYSYM_MOD_CTRL) == 0)
5092 goto other;
5093 if (prompt_parse(p, &pp) == NULL)
5094 break;
5095 if (pp.argv[pp.index] == NULL) {
5096 if (pp.index == 0) {
5097 prompt_parse_clean(&pp);
5098 break;
5100 --pp.index;
5102 handle_prompt_complete_clear();
5103 if (pp.argv[(pp.index + 1)] != NULL)
5104 prompt_replace(p, pp.argo[pp.index].pos,
5105 (pp.argo[(pp.index + 1)].pos -
5106 pp.argo[pp.index].pos),
5107 NULL, 0);
5108 else
5109 prompt_replace(p, pp.argo[pp.index].pos, ~0u, NULL, 0);
5110 p->cursor = pp.argo[pp.index].pos;
5111 prompt_parse_clean(&pp);
5112 break;
5113 case SDLK_RETURN:
5114 case SDLK_KP_ENTER:
5115 handle_prompt_complete_clear();
5116 ret |= handle_prompt_enter(megad);
5117 break;
5118 case SDLK_ESCAPE:
5119 handle_prompt_complete_clear();
5120 ret |= PROMPT_RET_EXIT;
5121 break;
5122 case SDLK_TAB:
5123 if (ksym & KEYSYM_MOD_SHIFT)
5124 ret |= handle_prompt_complete(megad, true);
5125 else
5126 ret |= handle_prompt_complete(megad, false);
5127 break;
5128 default:
5129 other:
5130 if (ksym_uni == 0)
5131 break;
5132 handle_prompt_complete_clear();
5133 sz = utf32u8(c, ksym_uni);
5134 if ((sz != 0) &&
5135 ((s = backslashify(c, sz, BACKSLASHIFY_NOQUOTES,
5136 NULL)) != NULL)) {
5137 size_t i;
5139 for (i = 0; (i != strlen(s)); ++i)
5140 prompt_put(p, s[i]);
5141 free(s);
5143 break;
5145 end:
5146 if ((ret & ~(PROMPT_RET_CONT | PROMPT_RET_ENTER)) == 0) {
5147 ph = &p->history[(p->current)];
5148 pd_message_cursor((p->cursor + 1),
5149 "%s%.*s", prompt_str, ph->length, ph->line);
5151 return ret;
5154 // Controls enum. You must add new entries at the end. Do not change the order.
5155 enum ctl_e {
5156 CTL_PAD1_UP,
5157 CTL_PAD1_DOWN,
5158 CTL_PAD1_LEFT,
5159 CTL_PAD1_RIGHT,
5160 CTL_PAD1_A,
5161 CTL_PAD1_B,
5162 CTL_PAD1_C,
5163 CTL_PAD1_X,
5164 CTL_PAD1_Y,
5165 CTL_PAD1_Z,
5166 CTL_PAD1_MODE,
5167 CTL_PAD1_START,
5168 CTL_PAD2_UP,
5169 CTL_PAD2_DOWN,
5170 CTL_PAD2_LEFT,
5171 CTL_PAD2_RIGHT,
5172 CTL_PAD2_A,
5173 CTL_PAD2_B,
5174 CTL_PAD2_C,
5175 CTL_PAD2_X,
5176 CTL_PAD2_Y,
5177 CTL_PAD2_Z,
5178 CTL_PAD2_MODE,
5179 CTL_PAD2_START,
5180 #ifdef WITH_PICO
5181 CTL_PICO_PEN_UP,
5182 CTL_PICO_PEN_DOWN,
5183 CTL_PICO_PEN_LEFT,
5184 CTL_PICO_PEN_RIGHT,
5185 CTL_PICO_PEN_BUTTON,
5186 #endif
5187 CTL_DGEN_QUIT,
5188 CTL_DGEN_CRAPTV_TOGGLE,
5189 CTL_DGEN_SCALING_TOGGLE,
5190 CTL_DGEN_RESET,
5191 CTL_DGEN_SLOT0,
5192 CTL_DGEN_SLOT1,
5193 CTL_DGEN_SLOT2,
5194 CTL_DGEN_SLOT3,
5195 CTL_DGEN_SLOT4,
5196 CTL_DGEN_SLOT5,
5197 CTL_DGEN_SLOT6,
5198 CTL_DGEN_SLOT7,
5199 CTL_DGEN_SLOT8,
5200 CTL_DGEN_SLOT9,
5201 CTL_DGEN_SLOT_NEXT,
5202 CTL_DGEN_SLOT_PREV,
5203 CTL_DGEN_SAVE,
5204 CTL_DGEN_LOAD,
5205 CTL_DGEN_Z80_TOGGLE,
5206 CTL_DGEN_CPU_TOGGLE,
5207 CTL_DGEN_STOP,
5208 CTL_DGEN_PROMPT,
5209 CTL_DGEN_GAME_GENIE,
5210 CTL_DGEN_VOLUME_INC,
5211 CTL_DGEN_VOLUME_DEC,
5212 CTL_DGEN_FULLSCREEN_TOGGLE,
5213 CTL_DGEN_FIX_CHECKSUM,
5214 CTL_DGEN_SCREENSHOT,
5215 CTL_DGEN_DEBUG_ENTER,
5216 CTL_
5219 // Controls definitions.
5220 struct ctl {
5221 const enum ctl_e type;
5222 intptr_t (*const rc)[RCB_NUM];
5223 int (*const press)(struct ctl&, md&);
5224 int (*const release)(struct ctl&, md&);
5225 #define DEF 0, 0, 0, 0
5226 unsigned int pressed:1;
5227 unsigned int coord:1;
5228 unsigned int x:10;
5229 unsigned int y:10;
5232 static int ctl_pad1(struct ctl& ctl, md& megad)
5234 switch (ctl.type) {
5235 case CTL_PAD1_UP:
5236 megad.pad[0] &= ~MD_UP_MASK;
5237 break;
5238 case CTL_PAD1_DOWN:
5239 megad.pad[0] &= ~MD_DOWN_MASK;
5240 break;
5241 case CTL_PAD1_LEFT:
5242 megad.pad[0] &= ~MD_LEFT_MASK;
5243 break;
5244 case CTL_PAD1_RIGHT:
5245 megad.pad[0] &= ~MD_RIGHT_MASK;
5246 break;
5247 case CTL_PAD1_A:
5248 megad.pad[0] &= ~MD_A_MASK;
5249 break;
5250 case CTL_PAD1_B:
5251 megad.pad[0] &= ~MD_B_MASK;
5252 break;
5253 case CTL_PAD1_C:
5254 megad.pad[0] &= ~MD_C_MASK;
5255 break;
5256 case CTL_PAD1_X:
5257 megad.pad[0] &= ~MD_X_MASK;
5258 break;
5259 case CTL_PAD1_Y:
5260 megad.pad[0] &= ~MD_Y_MASK;
5261 break;
5262 case CTL_PAD1_Z:
5263 megad.pad[0] &= ~MD_Z_MASK;
5264 break;
5265 case CTL_PAD1_MODE:
5266 megad.pad[0] &= ~MD_MODE_MASK;
5267 break;
5268 case CTL_PAD1_START:
5269 megad.pad[0] &= ~MD_START_MASK;
5270 break;
5271 default:
5272 break;
5274 return 1;
5277 static int ctl_pad1_release(struct ctl& ctl, md& megad)
5279 switch (ctl.type) {
5280 case CTL_PAD1_UP:
5281 megad.pad[0] |= MD_UP_MASK;
5282 break;
5283 case CTL_PAD1_DOWN:
5284 megad.pad[0] |= MD_DOWN_MASK;
5285 break;
5286 case CTL_PAD1_LEFT:
5287 megad.pad[0] |= MD_LEFT_MASK;
5288 break;
5289 case CTL_PAD1_RIGHT:
5290 megad.pad[0] |= MD_RIGHT_MASK;
5291 break;
5292 case CTL_PAD1_A:
5293 megad.pad[0] |= MD_A_MASK;
5294 break;
5295 case CTL_PAD1_B:
5296 megad.pad[0] |= MD_B_MASK;
5297 break;
5298 case CTL_PAD1_C:
5299 megad.pad[0] |= MD_C_MASK;
5300 break;
5301 case CTL_PAD1_X:
5302 megad.pad[0] |= MD_X_MASK;
5303 break;
5304 case CTL_PAD1_Y:
5305 megad.pad[0] |= MD_Y_MASK;
5306 break;
5307 case CTL_PAD1_Z:
5308 megad.pad[0] |= MD_Z_MASK;
5309 break;
5310 case CTL_PAD1_MODE:
5311 megad.pad[0] |= MD_MODE_MASK;
5312 break;
5313 case CTL_PAD1_START:
5314 megad.pad[0] |= MD_START_MASK;
5315 break;
5316 default:
5317 break;
5319 return 1;
5322 static int ctl_pad2(struct ctl& ctl, md& megad)
5324 switch (ctl.type) {
5325 case CTL_PAD2_UP:
5326 megad.pad[1] &= ~MD_UP_MASK;
5327 break;
5328 case CTL_PAD2_DOWN:
5329 megad.pad[1] &= ~MD_DOWN_MASK;
5330 break;
5331 case CTL_PAD2_LEFT:
5332 megad.pad[1] &= ~MD_LEFT_MASK;
5333 break;
5334 case CTL_PAD2_RIGHT:
5335 megad.pad[1] &= ~MD_RIGHT_MASK;
5336 break;
5337 case CTL_PAD2_A:
5338 megad.pad[1] &= ~MD_A_MASK;
5339 break;
5340 case CTL_PAD2_B:
5341 megad.pad[1] &= ~MD_B_MASK;
5342 break;
5343 case CTL_PAD2_C:
5344 megad.pad[1] &= ~MD_C_MASK;
5345 break;
5346 case CTL_PAD2_X:
5347 megad.pad[1] &= ~MD_X_MASK;
5348 break;
5349 case CTL_PAD2_Y:
5350 megad.pad[1] &= ~MD_Y_MASK;
5351 break;
5352 case CTL_PAD2_Z:
5353 megad.pad[1] &= ~MD_Z_MASK;
5354 break;
5355 case CTL_PAD2_MODE:
5356 megad.pad[1] &= ~MD_MODE_MASK;
5357 break;
5358 case CTL_PAD2_START:
5359 megad.pad[1] &= ~MD_START_MASK;
5360 break;
5361 default:
5362 break;
5364 return 1;
5367 static int ctl_pad2_release(struct ctl& ctl, md& megad)
5369 switch (ctl.type) {
5370 case CTL_PAD2_UP:
5371 megad.pad[1] |= MD_UP_MASK;
5372 break;
5373 case CTL_PAD2_DOWN:
5374 megad.pad[1] |= MD_DOWN_MASK;
5375 break;
5376 case CTL_PAD2_LEFT:
5377 megad.pad[1] |= MD_LEFT_MASK;
5378 break;
5379 case CTL_PAD2_RIGHT:
5380 megad.pad[1] |= MD_RIGHT_MASK;
5381 break;
5382 case CTL_PAD2_A:
5383 megad.pad[1] |= MD_A_MASK;
5384 break;
5385 case CTL_PAD2_B:
5386 megad.pad[1] |= MD_B_MASK;
5387 break;
5388 case CTL_PAD2_C:
5389 megad.pad[1] |= MD_C_MASK;
5390 break;
5391 case CTL_PAD2_X:
5392 megad.pad[1] |= MD_X_MASK;
5393 break;
5394 case CTL_PAD2_Y:
5395 megad.pad[1] |= MD_Y_MASK;
5396 break;
5397 case CTL_PAD2_Z:
5398 megad.pad[1] |= MD_Z_MASK;
5399 break;
5400 case CTL_PAD2_MODE:
5401 megad.pad[1] |= MD_MODE_MASK;
5402 break;
5403 case CTL_PAD2_START:
5404 megad.pad[1] |= MD_START_MASK;
5405 break;
5406 default:
5407 break;
5409 return 1;
5412 #ifdef WITH_PICO
5414 static int ctl_pico_pen(struct ctl& ctl, md& megad)
5416 static unsigned int min_y = 0x1fc;
5417 static unsigned int max_y = 0x2f7;
5418 static unsigned int min_x = 0x3c;
5419 static unsigned int max_x = 0x17c;
5420 static const struct {
5421 enum ctl_e type;
5422 unsigned int coords:1;
5423 unsigned int dir:1;
5424 unsigned int lim[2];
5425 } motion[] = {
5426 { CTL_PICO_PEN_UP, 1, 0, { min_y, max_y } },
5427 { CTL_PICO_PEN_DOWN, 1, 1, { min_y, max_y } },
5428 { CTL_PICO_PEN_LEFT, 0, 0, { min_x, max_x } },
5429 { CTL_PICO_PEN_RIGHT, 0, 1, { min_x, max_x } }
5431 unsigned int i;
5433 if (ctl.type == CTL_PICO_PEN_BUTTON) {
5434 megad.pad[0] &= ~MD_PICO_PENBTN_MASK;
5435 return 1;
5437 // Use coordinates if available.
5438 if ((ctl.coord) &&
5439 (screen.window_width != 0) &&
5440 (screen.window_height != 0)) {
5441 megad.pico_pen_coords[1] =
5442 (min_y + ((ctl.y * (max_y - min_y)) /
5443 screen.window_height));
5444 megad.pico_pen_coords[0] =
5445 (min_x + ((ctl.x * (max_x - min_x)) /
5446 screen.window_width));
5447 return 1;
5449 for (i = 0; (i != elemof(motion)); ++i) {
5450 unsigned int coords;
5452 if (motion[i].type != ctl.type)
5453 continue;
5454 coords = motion[i].coords;
5455 if (motion[i].dir)
5456 megad.pico_pen_coords[coords] += pico_pen_stride;
5457 else
5458 megad.pico_pen_coords[coords] -= pico_pen_stride;
5459 if ((megad.pico_pen_coords[coords] < motion[i].lim[0]) ||
5460 (megad.pico_pen_coords[coords] > motion[i].lim[1]))
5461 megad.pico_pen_coords[coords] =
5462 motion[i].lim[motion[i].dir];
5463 break;
5465 return 1;
5468 static int ctl_pico_pen_release(struct ctl& ctl, md& megad)
5470 if (ctl.type == CTL_PICO_PEN_BUTTON)
5471 megad.pad[0] |= MD_PICO_PENBTN_MASK;
5472 return 1;
5475 #endif
5477 static int ctl_dgen_quit(struct ctl&, md&)
5479 return 0;
5482 static int ctl_dgen_craptv_toggle(struct ctl&, md&)
5484 #ifdef WITH_CTV
5485 dgen_craptv = ((dgen_craptv + 1) % NUM_CTV);
5486 filters_pluck_ctv();
5487 filters_insert(filters_find(ctv_names[dgen_craptv]));
5488 pd_message("Crap TV mode \"%s\".", ctv_names[dgen_craptv]);
5489 #endif // WITH_CTV
5490 return 1;
5493 static int ctl_dgen_scaling_toggle(struct ctl&, md&)
5495 dgen_scaling = ((dgen_scaling + 1) % NUM_SCALING);
5496 if (set_scaling(scaling_names[dgen_scaling]))
5497 pd_message("Scaling algorithm \"%s\" unavailable.",
5498 scaling_names[dgen_scaling]);
5499 else
5500 pd_message("Using scaling algorithm \"%s\".",
5501 scaling_names[dgen_scaling]);
5502 return 1;
5505 static int ctl_dgen_reset(struct ctl&, md& megad)
5507 megad.reset();
5508 pd_message("Genesis reset.");
5509 return 1;
5512 static int ctl_dgen_slot(struct ctl& ctl, md&)
5514 slot = ((int)ctl.type - CTL_DGEN_SLOT0);
5515 pd_message("Selected save slot %d.", slot);
5516 return 1;
5519 static int ctl_dgen_slot_next(struct ctl&, md&)
5521 if (slot == 9)
5522 slot = 0;
5523 else
5524 slot++;
5525 pd_message("Selected next save slot (%d).", slot);
5526 return 1;
5529 static int ctl_dgen_slot_prev(struct ctl&, md&)
5531 if (slot == 0)
5532 slot = 9;
5533 else
5534 slot--;
5535 pd_message("Selected previous save slot (%d).", slot);
5536 return 1;
5539 static int ctl_dgen_save(struct ctl&, md& megad)
5541 md_save(megad);
5542 return 1;
5545 static int ctl_dgen_load(struct ctl&, md& megad)
5547 md_load(megad);
5548 return 1;
5551 // Cycle Z80 core.
5552 static int ctl_dgen_z80_toggle(struct ctl&, md& megad)
5554 const char *msg;
5556 megad.cycle_z80();
5557 switch (megad.z80_core) {
5558 #ifdef WITH_CZ80
5559 case md::Z80_CORE_CZ80:
5560 msg = "CZ80 core activated.";
5561 break;
5562 #endif
5563 #ifdef WITH_MZ80
5564 case md::Z80_CORE_MZ80:
5565 msg = "MZ80 core activated.";
5566 break;
5567 #endif
5568 #ifdef WITH_DRZ80
5569 case md::Z80_CORE_DRZ80:
5570 msg = "DrZ80 core activated.";
5571 break;
5572 #endif
5573 default:
5574 msg = "Z80 core disabled.";
5575 break;
5577 pd_message(msg);
5578 return 1;
5581 // Added this CPU core hot swap. Compile both Musashi and StarScream
5582 // in, and swap on the fly like DirectX DGen. [PKH]
5583 static int ctl_dgen_cpu_toggle(struct ctl&, md& megad)
5585 const char *msg;
5587 megad.cycle_cpu();
5588 switch (megad.cpu_emu) {
5589 #ifdef WITH_STAR
5590 case md::CPU_EMU_STAR:
5591 msg = "StarScream CPU core activated.";
5592 break;
5593 #endif
5594 #ifdef WITH_MUSA
5595 case md::CPU_EMU_MUSA:
5596 msg = "Musashi CPU core activated.";
5597 break;
5598 #endif
5599 #ifdef WITH_CYCLONE
5600 case md::CPU_EMU_CYCLONE:
5601 msg = "Cyclone CPU core activated.";
5602 break;
5603 #endif
5604 default:
5605 msg = "CPU core disabled.";
5606 break;
5608 pd_message(msg);
5609 return 1;
5612 static int ctl_dgen_stop(struct ctl&, md& megad)
5614 pd_message(stopped_str);
5615 if (stop_events(megad, STOPPED) != 0)
5616 return 0;
5617 return 1;
5620 static int ctl_dgen_prompt(struct ctl&, md& megad)
5622 pd_message_cursor(strlen(prompt_str), prompt_str);
5623 if (stop_events(megad, PROMPT) != 0)
5624 return 0;
5625 return 1;
5628 static int ctl_dgen_game_genie(struct ctl&, md& megad)
5630 pd_message_cursor(strlen(game_genie_str), game_genie_str);
5631 if (stop_events(megad, GAME_GENIE) != 0)
5632 return 0;
5633 return 1;
5636 static int ctl_dgen_volume(struct ctl& ctl, md&)
5638 if (ctl.type == CTL_DGEN_VOLUME_INC)
5639 ++dgen_volume;
5640 else
5641 --dgen_volume;
5642 if (dgen_volume < 0)
5643 dgen_volume = 0;
5644 else if (dgen_volume > 100)
5645 dgen_volume = 100;
5646 pd_message("Volume %d%%.", (int)dgen_volume);
5647 return 1;
5650 static int ctl_dgen_fullscreen_toggle(struct ctl&, md&)
5652 switch (set_fullscreen(!screen.is_fullscreen)) {
5653 case -2:
5654 fprintf(stderr,
5655 "sdl: fatal error while trying to change screen"
5656 " resolution.\n");
5657 return 0;
5658 case -1:
5659 pd_message("Failed to toggle fullscreen mode.");
5660 break;
5661 default:
5662 pd_message("Fullscreen mode toggled.");
5664 return 1;
5667 static int ctl_dgen_fix_checksum(struct ctl&, md& megad)
5669 pd_message("Checksum fixed.");
5670 megad.fix_rom_checksum();
5671 return 1;
5674 static int ctl_dgen_screenshot(struct ctl&, md& megad)
5676 do_screenshot(megad);
5677 return 1;
5680 static int ctl_dgen_debug_enter(struct ctl&, md& megad)
5682 #ifdef WITH_DEBUGGER
5683 stopped = 1;
5684 if (megad.debug_trap == false)
5685 megad.debug_enter();
5686 else
5687 megad.debug_leave();
5688 #else
5689 (void)megad;
5690 pd_message("Debugger support not built in.");
5691 #endif
5692 return 1;
5695 static struct ctl control[] = {
5696 // Array indices and control[].type must match enum ctl_e's order.
5697 { CTL_PAD1_UP, &pad1_up, ctl_pad1, ctl_pad1_release, DEF },
5698 { CTL_PAD1_DOWN, &pad1_down, ctl_pad1, ctl_pad1_release, DEF },
5699 { CTL_PAD1_LEFT, &pad1_left, ctl_pad1, ctl_pad1_release, DEF },
5700 { CTL_PAD1_RIGHT, &pad1_right, ctl_pad1, ctl_pad1_release, DEF },
5701 { CTL_PAD1_A, &pad1_a, ctl_pad1, ctl_pad1_release, DEF },
5702 { CTL_PAD1_B, &pad1_b, ctl_pad1, ctl_pad1_release, DEF },
5703 { CTL_PAD1_C, &pad1_c, ctl_pad1, ctl_pad1_release, DEF },
5704 { CTL_PAD1_X, &pad1_x, ctl_pad1, ctl_pad1_release, DEF },
5705 { CTL_PAD1_Y, &pad1_y, ctl_pad1, ctl_pad1_release, DEF },
5706 { CTL_PAD1_Z, &pad1_z, ctl_pad1, ctl_pad1_release, DEF },
5707 { CTL_PAD1_MODE, &pad1_mode, ctl_pad1, ctl_pad1_release, DEF },
5708 { CTL_PAD1_START, &pad1_start, ctl_pad1, ctl_pad1_release, DEF },
5709 { CTL_PAD2_UP, &pad2_up, ctl_pad2, ctl_pad2_release, DEF },
5710 { CTL_PAD2_DOWN, &pad2_down, ctl_pad2, ctl_pad2_release, DEF },
5711 { CTL_PAD2_LEFT, &pad2_left, ctl_pad2, ctl_pad2_release, DEF },
5712 { CTL_PAD2_RIGHT, &pad2_right, ctl_pad2, ctl_pad2_release, DEF },
5713 { CTL_PAD2_A, &pad2_a, ctl_pad2, ctl_pad2_release, DEF },
5714 { CTL_PAD2_B, &pad2_b, ctl_pad2, ctl_pad2_release, DEF },
5715 { CTL_PAD2_C, &pad2_c, ctl_pad2, ctl_pad2_release, DEF },
5716 { CTL_PAD2_X, &pad2_x, ctl_pad2, ctl_pad2_release, DEF },
5717 { CTL_PAD2_Y, &pad2_y, ctl_pad2, ctl_pad2_release, DEF },
5718 { CTL_PAD2_Z, &pad2_z, ctl_pad2, ctl_pad2_release, DEF },
5719 { CTL_PAD2_MODE, &pad2_mode, ctl_pad2, ctl_pad2_release, DEF },
5720 { CTL_PAD2_START, &pad2_start, ctl_pad2, ctl_pad2_release, DEF },
5721 #ifdef WITH_PICO
5722 { CTL_PICO_PEN_UP,
5723 &pico_pen_up, ctl_pico_pen, ctl_pico_pen_release, DEF },
5724 { CTL_PICO_PEN_DOWN,
5725 &pico_pen_down, ctl_pico_pen, ctl_pico_pen_release, DEF },
5726 { CTL_PICO_PEN_LEFT,
5727 &pico_pen_left, ctl_pico_pen, ctl_pico_pen_release, DEF },
5728 { CTL_PICO_PEN_RIGHT,
5729 &pico_pen_right, ctl_pico_pen, ctl_pico_pen_release, DEF },
5730 { CTL_PICO_PEN_BUTTON,
5731 &pico_pen_button, ctl_pico_pen, ctl_pico_pen_release, DEF },
5732 #endif
5733 { CTL_DGEN_QUIT, &dgen_quit, ctl_dgen_quit, NULL, DEF },
5734 { CTL_DGEN_CRAPTV_TOGGLE,
5735 &dgen_craptv_toggle, ctl_dgen_craptv_toggle, NULL, DEF },
5736 { CTL_DGEN_SCALING_TOGGLE,
5737 &dgen_scaling_toggle, ctl_dgen_scaling_toggle, NULL, DEF },
5738 { CTL_DGEN_RESET, &dgen_reset, ctl_dgen_reset, NULL, DEF },
5739 { CTL_DGEN_SLOT0, &dgen_slot_0, ctl_dgen_slot, NULL, DEF },
5740 { CTL_DGEN_SLOT1, &dgen_slot_1, ctl_dgen_slot, NULL, DEF },
5741 { CTL_DGEN_SLOT2, &dgen_slot_2, ctl_dgen_slot, NULL, DEF },
5742 { CTL_DGEN_SLOT3, &dgen_slot_3, ctl_dgen_slot, NULL, DEF },
5743 { CTL_DGEN_SLOT4, &dgen_slot_4, ctl_dgen_slot, NULL, DEF },
5744 { CTL_DGEN_SLOT5, &dgen_slot_5, ctl_dgen_slot, NULL, DEF },
5745 { CTL_DGEN_SLOT6, &dgen_slot_6, ctl_dgen_slot, NULL, DEF },
5746 { CTL_DGEN_SLOT7, &dgen_slot_7, ctl_dgen_slot, NULL, DEF },
5747 { CTL_DGEN_SLOT8, &dgen_slot_8, ctl_dgen_slot, NULL, DEF },
5748 { CTL_DGEN_SLOT9, &dgen_slot_9, ctl_dgen_slot, NULL, DEF },
5749 { CTL_DGEN_SLOT_NEXT, &dgen_slot_next, ctl_dgen_slot_next, NULL, DEF },
5750 { CTL_DGEN_SLOT_PREV, &dgen_slot_prev, ctl_dgen_slot_prev, NULL, DEF },
5751 { CTL_DGEN_SAVE, &dgen_save, ctl_dgen_save, NULL, DEF },
5752 { CTL_DGEN_LOAD, &dgen_load, ctl_dgen_load, NULL, DEF },
5753 { CTL_DGEN_Z80_TOGGLE,
5754 &dgen_z80_toggle, ctl_dgen_z80_toggle, NULL, DEF },
5755 { CTL_DGEN_CPU_TOGGLE,
5756 &dgen_cpu_toggle, ctl_dgen_cpu_toggle, NULL, DEF },
5757 { CTL_DGEN_STOP, &dgen_stop, ctl_dgen_stop, NULL, DEF },
5758 { CTL_DGEN_PROMPT, &dgen_prompt, ctl_dgen_prompt, NULL, DEF },
5759 { CTL_DGEN_GAME_GENIE,
5760 &dgen_game_genie, ctl_dgen_game_genie, NULL, DEF },
5761 { CTL_DGEN_VOLUME_INC,
5762 &dgen_volume_inc, ctl_dgen_volume, NULL, DEF },
5763 { CTL_DGEN_VOLUME_DEC,
5764 &dgen_volume_dec, ctl_dgen_volume, NULL, DEF },
5765 { CTL_DGEN_FULLSCREEN_TOGGLE,
5766 &dgen_fullscreen_toggle, ctl_dgen_fullscreen_toggle, NULL, DEF },
5767 { CTL_DGEN_FIX_CHECKSUM,
5768 &dgen_fix_checksum, ctl_dgen_fix_checksum, NULL, DEF },
5769 { CTL_DGEN_SCREENSHOT,
5770 &dgen_screenshot, ctl_dgen_screenshot, NULL, DEF },
5771 { CTL_DGEN_DEBUG_ENTER,
5772 &dgen_debug_enter, ctl_dgen_debug_enter, NULL, DEF },
5773 { CTL_, NULL, NULL, NULL, DEF }
5776 static struct {
5777 char const* name; ///< Controller button name.
5778 enum ctl_e const id[2]; ///< Controls indices in control[].
5779 bool once; ///< If button has been pressed once.
5780 bool twice; ///< If button has been pressed twice.
5781 enum rc_binding_type type; ///< Type of code.
5782 intptr_t code; ///< Temporary code.
5783 } calibration_steps[] = {
5784 { "START", { CTL_PAD1_START, CTL_PAD2_START },
5785 false, false, RCB_NUM, -1 },
5786 { "MODE", { CTL_PAD1_MODE, CTL_PAD2_MODE },
5787 false, false, RCB_NUM, -1 },
5788 { "A", { CTL_PAD1_A, CTL_PAD2_A },
5789 false, false, RCB_NUM, -1 },
5790 { "B", { CTL_PAD1_B, CTL_PAD2_B },
5791 false, false, RCB_NUM, -1 },
5792 { "C", { CTL_PAD1_C, CTL_PAD2_C },
5793 false, false, RCB_NUM, -1 },
5794 { "X", { CTL_PAD1_X, CTL_PAD2_X },
5795 false, false, RCB_NUM, -1 },
5796 { "Y", { CTL_PAD1_Y, CTL_PAD2_Y },
5797 false, false, RCB_NUM, -1 },
5798 { "Z", { CTL_PAD1_Z, CTL_PAD2_Z },
5799 false, false, RCB_NUM, -1 },
5800 { "UP", { CTL_PAD1_UP, CTL_PAD2_UP },
5801 false, false, RCB_NUM, -1 },
5802 { "DOWN", { CTL_PAD1_DOWN, CTL_PAD2_DOWN },
5803 false, false, RCB_NUM, -1 },
5804 { "LEFT", { CTL_PAD1_LEFT, CTL_PAD2_LEFT },
5805 false, false, RCB_NUM, -1 },
5806 { "RIGHT", { CTL_PAD1_RIGHT, CTL_PAD2_RIGHT },
5807 false, false, RCB_NUM, -1 },
5808 { NULL, { CTL_, CTL_ },
5809 false, false, RCB_NUM, -1 }
5813 * Handle input during calibration process.
5814 * @param type Type of code.
5815 * @param code Code to process.
5817 static void manage_calibration(enum rc_binding_type type, intptr_t code)
5819 unsigned int step = 0;
5821 assert(calibrating_controller < 2);
5822 if (!calibrating) {
5823 // Stop emulation, enter calibration mode.
5824 freeze(true);
5825 calibrating = true;
5826 filter_text_str[0] = '\0';
5827 filter_text_msg(FILTER_TEXT_BG_BLACK
5828 FILTER_TEXT_CENTER
5829 FILTER_TEXT_8X13
5830 "CONTROLLER %u CALIBRATION\n"
5831 "\n"
5832 FILTER_TEXT_7X6
5833 FILTER_TEXT_LEFT
5834 "Press each button twice,\n"
5835 "or two different buttons to skip them.\n"
5836 "\n",
5837 (calibrating_controller + 1));
5838 filters_pluck(&filter_text_def);
5839 filters_insert(&filter_text_def);
5840 goto ask;
5842 while (step != elemof(calibration_steps))
5843 if ((calibration_steps[step].once == true) &&
5844 (calibration_steps[step].twice == true))
5845 ++step;
5846 else
5847 break;
5848 if (step == elemof(calibration_steps)) {
5849 // Reset everything.
5850 for (step = 0; (step != elemof(calibration_steps)); ++step) {
5851 calibration_steps[step].once = false;
5852 calibration_steps[step].twice = false;
5853 calibration_steps[step].type = RCB_NUM;
5854 calibration_steps[step].code = -1;
5856 // Restart emulation.
5857 freeze(false);
5858 calibrating = false;
5859 filters_pluck(&filter_text_def);
5860 return;
5862 if (calibration_steps[step].once == false) {
5863 char *dump;
5865 if (type == RCBK)
5866 dump = dump_keysym(code);
5867 else if (type == RCBJ)
5868 dump = dump_joypad(code);
5869 else if (type == RCBM)
5870 dump = dump_mouse(code);
5871 else
5872 dump = NULL;
5873 assert(calibration_steps[step].twice == false);
5874 calibration_steps[step].once = true;
5875 calibration_steps[step].type = type;
5876 calibration_steps[step].code = code;
5877 filter_text_msg("\"%s\", confirm: ", (dump ? dump : ""));
5878 free(dump);
5880 else if (calibration_steps[step].twice == false) {
5881 calibration_steps[step].twice = true;
5882 if ((calibration_steps[step].type == type) &&
5883 (calibration_steps[step].code == code))
5884 filter_text_msg("OK\n");
5885 else {
5886 calibration_steps[step].type = RCB_NUM;
5887 calibration_steps[step].code = -1;
5888 filter_text_msg("none\n");
5891 if ((calibration_steps[step].once != true) ||
5892 (calibration_steps[step].twice != true))
5893 return;
5894 ++step;
5895 ask:
5896 if (step == elemof(calibration_steps)) {
5897 code = calibration_steps[(elemof(calibration_steps) - 1)].code;
5898 if (code == -1)
5899 filter_text_msg("\n"
5900 "Aborted.");
5901 else {
5902 unsigned int i;
5904 for (i = 0; (i != elemof(calibration_steps)); ++i) {
5905 enum ctl_e id;
5907 id = calibration_steps[i].id
5908 [calibrating_controller];
5909 type = calibration_steps[i].type;
5910 code = calibration_steps[i].code;
5911 assert((size_t)id < elemof(control));
5912 assert(control[id].type == id);
5913 if ((id != CTL_) && (type != RCB_NUM))
5914 (*control[id].rc)[type] = code;
5916 filter_text_msg("\n"
5917 "Applied.");
5920 else if (calibration_steps[step].name != NULL)
5921 filter_text_msg("%s: ", calibration_steps[step].name);
5922 else
5923 filter_text_msg("\n"
5924 "Press any button twice to apply settings:\n"
5925 "");
5928 static struct rc_binding_item combos[64];
5930 static void manage_combos(md& md, bool pressed, enum rc_binding_type type,
5931 intptr_t code)
5933 unsigned int i;
5935 (void)md;
5936 for (i = 0; (i != elemof(combos)); ++i) {
5937 if (!combos[i].assigned) {
5938 if (!pressed)
5939 return; // Not in the list, nothing to do.
5940 // Not found, add it to the list.
5941 combos[i].assigned = true;
5942 combos[i].type = type;
5943 combos[i].code = code;
5944 return;
5946 if ((combos[i].type != type) || (combos[i].code != code))
5947 continue; // Does not match.
5948 if (pressed)
5949 return; // Already pressed.
5950 // Release entry.
5951 memmove(&combos[i], &combos[i + 1],
5952 ((elemof(combos) - (i + 1)) * sizeof(combos[i])));
5953 break;
5957 static bool check_combos(md& md, struct rc_binding_item item[],
5958 unsigned int num)
5960 unsigned int i;
5961 unsigned int found = 0;
5963 (void)md;
5964 for (i = 0; (i != num); ++i) {
5965 unsigned int j;
5967 if (!item[i].assigned) {
5968 num = i;
5969 break;
5971 for (j = 0; (j != elemof(combos)); ++j) {
5972 if (!combos[j].assigned)
5973 break;
5974 if ((combos[j].type != item[i].type) ||
5975 (combos[j].code != item[i].code))
5976 continue;
5977 ++found;
5978 break;
5981 if (num == 0)
5982 return false;
5983 return (found == num);
5986 static int manage_bindings(md& md, bool pressed, enum rc_binding_type type,
5987 intptr_t code)
5989 struct rc_binding *rcb = rc_binding_head.next;
5990 size_t pos = 0;
5991 size_t seek = 0;
5993 if ((dgen_buttons) && (pressed)) {
5994 char *dump;
5996 if (type == RCBK)
5997 dump = dump_keysym(code);
5998 else if (type == RCBJ)
5999 dump = dump_joypad(code);
6000 else if (type == RCBM)
6001 dump = dump_mouse(code);
6002 else
6003 dump = NULL;
6004 if (dump != NULL) {
6005 pd_message("Pressed \"%s\".", dump);
6006 free(dump);
6009 while (rcb != &rc_binding_head) {
6010 if ((pos < seek) ||
6011 (!check_combos(md, rcb->item, elemof(rcb->item)))) {
6012 ++pos;
6013 rcb = rcb->next;
6014 continue;
6016 assert(rcb->to != NULL);
6017 assert((intptr_t)rcb->to != -1);
6018 // For keyboard and joystick bindings, perform related action.
6019 if ((type = RCBK, !strncasecmp("key_", rcb->to, 4)) ||
6020 (type = RCBJ, !strncasecmp("joy_", rcb->to, 4)) ||
6021 (type = RCBM, !strncasecmp("mou_", rcb->to, 4))) {
6022 struct rc_field *rcf = rc_fields;
6024 while (rcf->fieldname != NULL) {
6025 struct ctl *ctl = control;
6027 if (strcasecmp(rcb->to, rcf->fieldname)) {
6028 ++rcf;
6029 continue;
6031 while (ctl->rc != NULL) {
6032 if (&(*ctl->rc)[type] !=
6033 rcf->variable) {
6034 ++ctl;
6035 continue;
6037 // Got it, finally.
6038 if (pressed) {
6039 assert(ctl->press != NULL);
6040 if (!ctl->press(*ctl, md))
6041 return 0;
6043 else if (ctl->release != NULL) {
6044 if (!ctl->release(*ctl, md))
6045 return 0;
6047 break;
6049 break;
6052 // Otherwise, pass it to the prompt.
6053 else if (pressed) {
6054 handle_prompt_complete_clear();
6055 prompt_replace(&prompt.status, 0, 0,
6056 (uint8_t *)rcb->to, strlen(rcb->to));
6057 if (handle_prompt_enter(md) & PROMPT_RET_ERROR)
6058 return 0;
6060 // In case the current (or any other binding) has been
6061 // removed, rewind and seek to the next position.
6062 rcb = rc_binding_head.next;
6063 seek = (pos + 1);
6064 pos = 0;
6066 return 1;
6069 static int manage_game_genie(md& megad, intptr_t ksym, intptr_t ksym_uni)
6071 static char buf[12];
6072 static kb_input_t input = { buf, 0, sizeof(buf) };
6073 unsigned int len = strlen(game_genie_str);
6075 switch (kb_input(&input, ksym, ksym_uni)) {
6076 unsigned int errors;
6077 unsigned int applied;
6078 unsigned int reverted;
6080 case KB_INPUT_ENTERED:
6081 megad.patch(input.buf, &errors, &applied, &reverted);
6082 if (errors)
6083 pd_message("Invalid code.");
6084 else if (reverted)
6085 pd_message("Reverted.");
6086 else if (applied)
6087 pd_message("Applied.");
6088 else {
6089 case KB_INPUT_ABORTED:
6090 pd_message("Aborted.");
6092 goto over;
6093 case KB_INPUT_CONSUMED:
6094 pd_message_cursor((len + input.pos), "%s%.*s", game_genie_str,
6095 (int)input.pos, buf);
6096 break;
6097 case KB_INPUT_IGNORED:
6098 break;
6100 return 0;
6101 over:
6102 input.buf = buf;
6103 input.pos = 0;
6104 input.size = sizeof(buf);
6105 memset(buf, 0, sizeof(buf));
6106 return 1;
6109 #ifdef WITH_PICO
6111 static void manage_pico_pen(md& megad)
6113 static unsigned long pico_pen_last_update;
6114 unsigned long pico_pen_now;
6116 if (!megad.pico_enabled)
6117 return;
6118 // Repeat pen motion as long as buttons are not released.
6119 // This is not necessary when pen is managed by direct coordinates.
6120 if ((((control[CTL_PICO_PEN_UP].pressed) &&
6121 (!control[CTL_PICO_PEN_UP].coord)) ||
6122 ((control[CTL_PICO_PEN_DOWN].pressed) &&
6123 (!control[CTL_PICO_PEN_DOWN].coord)) ||
6124 ((control[CTL_PICO_PEN_LEFT].pressed) &&
6125 (!control[CTL_PICO_PEN_LEFT].coord)) ||
6126 ((control[CTL_PICO_PEN_RIGHT].pressed) &&
6127 (!control[CTL_PICO_PEN_RIGHT].coord))) &&
6128 (pico_pen_now = pd_usecs(),
6129 ((pico_pen_now - pico_pen_last_update) >=
6130 ((unsigned long)pico_pen_delay * 1000)))) {
6131 if (control[CTL_PICO_PEN_UP].pressed)
6132 ctl_pico_pen
6133 (control[CTL_PICO_PEN_UP], megad);
6134 if (control[CTL_PICO_PEN_DOWN].pressed)
6135 ctl_pico_pen
6136 (control[CTL_PICO_PEN_DOWN], megad);
6137 if (control[CTL_PICO_PEN_LEFT].pressed)
6138 ctl_pico_pen
6139 (control[CTL_PICO_PEN_LEFT], megad);
6140 if (control[CTL_PICO_PEN_RIGHT].pressed)
6141 ctl_pico_pen
6142 (control[CTL_PICO_PEN_RIGHT], megad);
6143 pico_pen_last_update = pico_pen_now;
6147 #endif
6149 static bool mouse_is_grabbed()
6151 return (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON);
6154 static void mouse_grab(bool grab)
6156 SDL_GrabMode mode = SDL_WM_GrabInput(SDL_GRAB_QUERY);
6158 if ((grab) && (!pd_freeze) && (mode == SDL_GRAB_OFF)) {
6159 // Hide the cursor.
6160 SDL_ShowCursor(0);
6161 pd_message("Mouse trapped. Stop emulation to release.");
6162 SDL_WM_GrabInput(SDL_GRAB_ON);
6164 else if ((!grab) && (mode == SDL_GRAB_ON)) {
6165 SDL_ShowCursor(1);
6166 SDL_WM_GrabInput(SDL_GRAB_OFF);
6170 static int stop_events(md& megad, enum events status)
6172 struct ctl* ctl;
6174 stopped = 1;
6175 freeze(true);
6176 events = status;
6177 // Release controls.
6178 for (ctl = control; (ctl->rc != NULL); ++ctl) {
6179 if (ctl->pressed == false)
6180 continue;
6181 ctl->pressed = false;
6182 ctl->coord = false;
6183 if ((ctl->release != NULL) &&
6184 (ctl->release(*ctl, megad) == 0))
6185 return -1; // XXX do something about this.
6187 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
6188 SDL_DEFAULT_REPEAT_INTERVAL);
6189 // Switch out of fullscreen mode (assuming this is supported)
6190 if (screen.is_fullscreen) {
6191 if (set_fullscreen(0) < -1)
6192 return -1;
6193 pd_graphics_update(true);
6195 mouse_grab(false);
6196 return 0;
6199 static void restart_events(md& megad)
6201 (void)megad;
6202 stopped = 1;
6203 freeze(false);
6204 handle_prompt_complete_clear();
6205 SDL_EnableKeyRepeat(0, 0);
6206 events = STARTED;
6209 static struct {
6210 unsigned long when[0x100];
6211 uint8_t enabled[0x100 / 8];
6212 unsigned int count;
6213 } mouse_motion_release;
6215 #define MOUSE_MOTION_RELEASE_IS_ENABLED(which) \
6216 (mouse_motion_release.enabled[(which) / 8] & (1 << ((which) % 8)))
6217 #define MOUSE_MOTION_RELEASE_DISABLE(which) \
6218 (mouse_motion_release.enabled[(which) / 8] &= ~(1 << ((which) % 8)))
6219 #define MOUSE_MOTION_RELEASE_ENABLE(which) \
6220 (mouse_motion_release.enabled[(which) / 8] |= (1 << ((which) % 8)))
6222 static void mouse_motion_delay_release(unsigned int which, bool enable)
6224 if (which >= elemof(mouse_motion_release.when)) {
6225 DEBUG(("mouse index too high (%u)", which));
6226 return;
6228 if (!enable) {
6229 if (!MOUSE_MOTION_RELEASE_IS_ENABLED(which))
6230 return;
6231 MOUSE_MOTION_RELEASE_DISABLE(which);
6232 assert(mouse_motion_release.count != 0);
6233 --mouse_motion_release.count;
6234 return;
6236 if (!MOUSE_MOTION_RELEASE_IS_ENABLED(which)) {
6237 MOUSE_MOTION_RELEASE_ENABLE(which);
6238 ++mouse_motion_release.count;
6239 assert(mouse_motion_release.count <=
6240 elemof(mouse_motion_release.when));
6242 mouse_motion_release.when[which] =
6243 (pd_usecs() + (dgen_mouse_delay * 1000));
6246 static bool mouse_motion_released(SDL_Event *event)
6248 unsigned int i;
6249 unsigned long now;
6251 if (mouse_motion_release.count == 0)
6252 return false;
6253 now = pd_usecs();
6254 for (i = 0; (i != mouse_motion_release.count); ++i) {
6255 unsigned long diff;
6257 if (!MOUSE_MOTION_RELEASE_IS_ENABLED(i))
6258 continue;
6259 diff = (mouse_motion_release.when[i] - now);
6260 if (diff < (unsigned long)(dgen_mouse_delay * 1000))
6261 continue;
6262 event->motion.type = SDL_MOUSEMOTION;
6263 event->motion.which = i;
6264 event->motion.xrel = 0;
6265 event->motion.yrel = 0;
6266 MOUSE_MOTION_RELEASE_DISABLE(i);
6267 --mouse_motion_release.count;
6268 return true;
6270 return false;
6273 #define MOUSE_SHOW_USECS (unsigned long)(2 * 1000000)
6275 // The massive event handler!
6276 // I know this is an ugly beast, but please don't be discouraged. If you need
6277 // help, don't be afraid to ask me how something works. Basically, just handle
6278 // all the event keys, or even ignore a few if they don't make sense for your
6279 // interface.
6280 int pd_handle_events(md &megad)
6282 static uint16_t kpress[0x100];
6283 #ifdef WITH_DEBUGGER
6284 static bool debug_trap;
6285 #endif
6286 static unsigned long hide_mouse_when;
6287 static bool hide_mouse;
6288 #ifdef WITH_JOYSTICK
6289 static uint32_t const axis_value[][3] = {
6290 // { pressed, [implicitly released ...] }
6291 { JS_AXIS_NEGATIVE, JS_AXIS_BETWEEN, JS_AXIS_POSITIVE },
6292 { JS_AXIS_POSITIVE, JS_AXIS_BETWEEN, JS_AXIS_NEGATIVE },
6293 { JS_AXIS_BETWEEN, JS_AXIS_POSITIVE, JS_AXIS_NEGATIVE }
6295 static uint32_t const hat_value[][2] = {
6296 // { SDL value, pressed }
6297 { SDL_HAT_UP, JS_HAT_UP },
6298 { SDL_HAT_RIGHT, JS_HAT_RIGHT },
6299 { SDL_HAT_DOWN, JS_HAT_DOWN },
6300 { SDL_HAT_LEFT, JS_HAT_LEFT }
6302 unsigned int hat_value_map;
6303 intptr_t joypad;
6304 bool pressed;
6305 #endif
6306 uint32_t plist[8];
6307 uint32_t rlist[8];
6308 unsigned int i, pi, ri;
6309 SDL_Event event;
6310 uint16_t ksym_uni;
6311 intptr_t ksym;
6312 intptr_t mouse;
6313 unsigned int which;
6315 #ifdef WITH_DEBUGGER
6316 if ((megad.debug_trap) && (megad.debug_enter() < 0))
6317 return 0;
6318 if (debug_trap != megad.debug_trap) {
6319 debug_trap = megad.debug_trap;
6320 if (debug_trap)
6321 mouse_grab(false);
6322 if (sound.cbuf.size)
6323 SDL_PauseAudio(debug_trap == true);
6325 #endif
6326 if ((hide_mouse) &&
6327 ((hide_mouse_when - pd_usecs()) >= MOUSE_SHOW_USECS)) {
6328 if (!mouse_is_grabbed())
6329 SDL_ShowCursor(0);
6330 hide_mouse = false;
6332 next_event:
6333 if (mouse_motion_released(&event))
6334 goto mouse_motion;
6335 if (!SDL_PollEvent(&event)) {
6336 #ifdef WITH_PICO
6337 manage_pico_pen(megad);
6338 #endif
6339 return 1;
6341 switch (event.type) {
6342 #ifdef WITH_JOYSTICK
6343 case SDL_JOYAXISMOTION:
6344 if (event.jaxis.value <= -16384)
6345 i = 0;
6346 else if (event.jaxis.value >= 16384)
6347 i = 1;
6348 else
6349 i = 2;
6350 plist[0] = JS_AXIS(event.jaxis.which,
6351 event.jaxis.axis,
6352 axis_value[i][0]);
6353 rlist[0] = JS_AXIS(event.jaxis.which,
6354 event.jaxis.axis,
6355 axis_value[i][1]);
6356 rlist[1] = JS_AXIS(event.jaxis.which,
6357 event.jaxis.axis,
6358 axis_value[i][2]);
6359 // "between" causes problems during calibration, ignore it.
6360 if (axis_value[i][0] == JS_AXIS_BETWEEN)
6361 pi = 0;
6362 else
6363 pi = 1;
6364 ri = 2;
6365 goto joypad_axis;
6366 case SDL_JOYHATMOTION:
6367 pi = 0;
6368 ri = 0;
6369 hat_value_map = 0;
6370 for (i = 0; (i != elemof(hat_value)); ++i)
6371 if (event.jhat.value & hat_value[i][0]) {
6372 plist[pi++] = JS_HAT(event.jhat.which,
6373 event.jhat.hat,
6374 hat_value[i][1]);
6375 hat_value_map |= (1 << i);
6377 for (i = 0; (i != elemof(hat_value)); ++i)
6378 if ((hat_value_map & (1 << i)) == 0)
6379 rlist[ri++] = JS_HAT(event.jhat.which,
6380 event.jhat.hat,
6381 hat_value[i][1]);
6382 joypad_axis:
6383 for (i = 0; (i != ri); ++i)
6384 manage_combos(megad, false, RCBJ, rlist[i]);
6385 for (i = 0; (i != pi); ++i)
6386 manage_combos(megad, true, RCBJ, plist[i]);
6387 if (events != STARTED)
6388 break;
6389 if (calibrating) {
6390 for (i = 0; ((calibrating) && (i != pi)); ++i)
6391 manage_calibration(RCBJ, plist[i]);
6392 break;
6394 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6395 // Release buttons first.
6396 for (i = 0; (i != ri); ++i) {
6397 if ((ctl->pressed == false) ||
6398 ((uint32_t)(*ctl->rc)[RCBJ] != rlist[i]))
6399 continue;
6400 ctl->pressed = false;
6401 ctl->coord = false;
6402 if ((ctl->release != NULL) &&
6403 (ctl->release(*ctl, megad) == 0))
6404 return 0;
6406 for (i = 0; (i != pi); ++i) {
6407 if ((uint32_t)(*ctl->rc)[RCBJ] == plist[i]) {
6408 assert(ctl->press != NULL);
6409 ctl->pressed = true;
6410 ctl->coord = false;
6411 if (ctl->press(*ctl, megad) == 0)
6412 return 0;
6416 for (i = 0; (i != ri); ++i)
6417 if (!manage_bindings(megad, false, RCBJ, rlist[i]))
6418 return 0;
6419 for (i = 0; (i != pi); ++i)
6420 if (!manage_bindings(megad, true, RCBJ, plist[i]))
6421 return 0;
6422 break;
6423 case SDL_JOYBUTTONDOWN:
6424 assert(event.jbutton.state == SDL_PRESSED);
6425 pressed = true;
6426 goto joypad_button;
6427 case SDL_JOYBUTTONUP:
6428 assert(event.jbutton.state == SDL_RELEASED);
6429 pressed = false;
6430 joypad_button:
6431 joypad = JS_BUTTON(event.jbutton.which, event.jbutton.button);
6432 manage_combos(megad, pressed, RCBJ, joypad);
6433 if (events != STARTED)
6434 break;
6435 if (calibrating) {
6436 if (pressed)
6437 manage_calibration(RCBJ, joypad);
6438 break;
6440 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6441 if ((*ctl->rc)[RCBJ] != joypad)
6442 continue;
6443 ctl->pressed = pressed;
6444 ctl->coord = false;
6445 if (pressed == false) {
6446 if ((ctl->release != NULL) &&
6447 (ctl->release(*ctl, megad) == 0))
6448 return 0;
6450 else {
6451 assert(ctl->press != NULL);
6452 if (ctl->press(*ctl, megad) == 0)
6453 return 0;
6456 if (manage_bindings(megad, pressed, RCBJ, joypad) == 0)
6457 return 0;
6458 break;
6459 #endif // WITH_JOYSTICK
6460 case SDL_KEYDOWN:
6461 ksym = event.key.keysym.sym;
6462 ksym_uni = event.key.keysym.unicode;
6463 if ((ksym_uni < 0x20) ||
6464 ((ksym >= SDLK_KP0) && (ksym <= SDLK_KP_EQUALS)))
6465 ksym_uni = 0;
6466 kpress[(ksym & 0xff)] = ksym_uni;
6467 if (ksym_uni)
6468 ksym = ksym_uni;
6469 else if (event.key.keysym.mod & KMOD_SHIFT)
6470 ksym |= KEYSYM_MOD_SHIFT;
6472 // Check for modifiers
6473 if (event.key.keysym.mod & KMOD_CTRL)
6474 ksym |= KEYSYM_MOD_CTRL;
6475 if (event.key.keysym.mod & KMOD_ALT)
6476 ksym |= KEYSYM_MOD_ALT;
6477 if (event.key.keysym.mod & KMOD_META)
6478 ksym |= KEYSYM_MOD_META;
6480 manage_combos(megad, true, RCBK, ksym);
6482 if (calibrating) {
6483 manage_calibration(RCBK, ksym);
6484 break;
6487 switch (events) {
6488 int ret;
6490 case STARTED:
6491 break;
6492 case PROMPT:
6493 case STOPPED_PROMPT:
6494 ret = handle_prompt(ksym, ksym_uni, megad);
6495 if (ret & PROMPT_RET_ERROR) {
6496 restart_events(megad);
6497 return 0;
6499 if (ret & PROMPT_RET_EXIT) {
6500 if (events == STOPPED_PROMPT) {
6501 // Return to stopped mode.
6502 pd_message(stopped_str);
6503 events = STOPPED;
6504 goto next_event;
6506 if ((ret & PROMPT_RET_MSG) == 0)
6507 pd_message("RUNNING.");
6508 restart_events(megad);
6509 goto next_event;
6511 if (ret & PROMPT_RET_ENTER) {
6512 // Back to the prompt only in stopped mode.
6513 if (events == STOPPED_PROMPT)
6514 goto next_event;
6515 if ((ret & PROMPT_RET_MSG) == 0)
6516 pd_message("");
6517 restart_events(megad);
6518 goto next_event;
6520 // PROMPT_RET_CONT
6521 goto next_event;
6522 case GAME_GENIE:
6523 case STOPPED_GAME_GENIE:
6524 if (manage_game_genie(megad, ksym, ksym_uni) == 0)
6525 goto next_event;
6526 if (events == STOPPED_GAME_GENIE) {
6527 // Return to stopped mode.
6528 pd_message(stopped_str);
6529 events = STOPPED;
6531 else
6532 restart_events(megad);
6533 goto next_event;
6534 case STOPPED:
6535 // In basic stopped mode, handle a few keysyms.
6536 if (ksym == dgen_game_genie[0]) {
6537 pd_message_cursor(strlen(game_genie_str),
6538 game_genie_str);
6539 events = STOPPED_GAME_GENIE;
6541 else if (ksym == dgen_prompt[0]) {
6542 pd_message_cursor(strlen(prompt_str),
6543 prompt_str);
6544 events = STOPPED_PROMPT;
6546 else if (ksym == dgen_quit[0]) {
6547 restart_events(megad);
6548 return 0;
6550 else if (ksym == dgen_stop[0]) {
6551 pd_message("RUNNING.");
6552 restart_events(megad);
6554 #ifdef WITH_DEBUGGER
6555 else if (ksym == dgen_debug_enter[0])
6556 ctl_dgen_debug_enter(*(struct ctl *)0, megad);
6557 #endif
6558 default:
6559 goto next_event;
6562 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6563 if (ksym != (*ctl->rc)[RCBK])
6564 continue;
6565 assert(ctl->press != NULL);
6566 ctl->pressed = true;
6567 ctl->coord = false;
6568 if (ctl->press(*ctl, megad) == 0)
6569 return 0;
6571 if (manage_bindings(megad, true, RCBK, ksym) == 0)
6572 return 0;
6573 break;
6574 case SDL_KEYUP:
6575 ksym = event.key.keysym.sym;
6576 ksym_uni = kpress[(ksym & 0xff)];
6577 if ((ksym_uni < 0x20) ||
6578 ((ksym >= SDLK_KP0) && (ksym <= SDLK_KP_EQUALS)))
6579 ksym_uni = 0;
6580 kpress[(ksym & 0xff)] = 0;
6581 if (ksym_uni)
6582 ksym = ksym_uni;
6584 manage_combos(megad, false, RCBK, ksym);
6585 manage_combos(megad, false, RCBK, (ksym | KEYSYM_MOD_ALT));
6586 manage_combos(megad, false, RCBK, (ksym | KEYSYM_MOD_SHIFT));
6587 manage_combos(megad, false, RCBK, (ksym | KEYSYM_MOD_CTRL));
6588 manage_combos(megad, false, RCBK, (ksym | KEYSYM_MOD_META));
6590 if (calibrating)
6591 break;
6592 if (events != STARTED)
6593 break;
6595 // The only time we care about key releases is for the
6596 // controls, but ignore key modifiers so they never get stuck.
6597 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6598 if (ksym != ((*ctl->rc)[RCBK] & ~KEYSYM_MOD_MASK))
6599 continue;
6600 ctl->pressed = false;
6601 ctl->coord = false;
6602 if ((ctl->release != NULL) &&
6603 (ctl->release(*ctl, megad) == 0))
6604 return 0;
6606 if (manage_bindings(megad, false, RCBK, ksym) == 0)
6607 return 0;
6608 break;
6609 case SDL_MOUSEMOTION:
6610 if (!mouse_is_grabbed()) {
6611 // Only show mouse pointer for a few seconds.
6612 SDL_ShowCursor(1);
6613 hide_mouse_when = (pd_usecs() + MOUSE_SHOW_USECS);
6614 hide_mouse = true;
6615 break;
6617 mouse_motion:
6618 which = event.motion.which;
6619 pi = 0;
6620 ri = 0;
6621 if (event.motion.xrel < 0) {
6622 plist[pi++] = MO_MOTION(which, 'l');
6623 rlist[ri++] = MO_MOTION(which, 'r');
6625 else if (event.motion.xrel > 0) {
6626 plist[pi++] = MO_MOTION(which, 'r');
6627 rlist[ri++] = MO_MOTION(which, 'l');
6629 else {
6630 rlist[ri++] = MO_MOTION(which, 'r');
6631 rlist[ri++] = MO_MOTION(which, 'l');
6633 if (event.motion.yrel < 0) {
6634 plist[pi++] = MO_MOTION(which, 'u');
6635 rlist[ri++] = MO_MOTION(which, 'd');
6637 else if (event.motion.yrel > 0) {
6638 plist[pi++] = MO_MOTION(which, 'd');
6639 rlist[ri++] = MO_MOTION(which, 'u');
6641 else {
6642 rlist[ri++] = MO_MOTION(which, 'd');
6643 rlist[ri++] = MO_MOTION(which, 'u');
6645 if (pi)
6646 mouse_motion_delay_release(which, true);
6647 else
6648 mouse_motion_delay_release(which, false);
6649 for (i = 0; (i != ri); ++i)
6650 manage_combos(megad, false, RCBM, rlist[i]);
6651 for (i = 0; (i != pi); ++i)
6652 manage_combos(megad, true, RCBM, plist[i]);
6653 if (calibrating) {
6654 for (i = 0; ((calibrating) && (i != pi)); ++i)
6655 manage_calibration(RCBM, plist[i]);
6656 break;
6658 if (events != STARTED)
6659 break;
6660 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6661 // Release buttons first.
6662 for (i = 0; (i != ri); ++i) {
6663 if ((ctl->pressed == false) ||
6664 ((uint32_t)(*ctl->rc)[RCBM] != rlist[i]))
6665 continue;
6666 ctl->pressed = false;
6667 ctl->coord = true;
6668 ctl->x = event.motion.x;
6669 ctl->y = event.motion.y;
6670 if ((ctl->release != NULL) &&
6671 (ctl->release(*ctl, megad) == 0))
6672 return 0;
6674 for (i = 0; (i != pi); ++i) {
6675 if ((uint32_t)(*ctl->rc)[RCBM] == plist[i]) {
6676 assert(ctl->press != NULL);
6677 ctl->pressed = true;
6678 ctl->coord = true;
6679 ctl->x = event.motion.x;
6680 ctl->y = event.motion.y;
6681 if (ctl->press(*ctl, megad) == 0)
6682 return 0;
6686 for (i = 0; (i != ri); ++i)
6687 if (!manage_bindings(megad, false, RCBM, rlist[i]))
6688 return 0;
6689 for (i = 0; (i != pi); ++i)
6690 if (!manage_bindings(megad, true, RCBM, plist[i]))
6691 return 0;
6692 break;
6693 case SDL_MOUSEBUTTONDOWN:
6694 assert(event.button.state == SDL_PRESSED);
6695 #ifdef WITH_DEBUGGER
6696 if (!debug_trap)
6697 #endif
6698 mouse_grab(true);
6699 pressed = true;
6700 goto mouse_button;
6701 case SDL_MOUSEBUTTONUP:
6702 assert(event.button.state == SDL_RELEASED);
6703 pressed = false;
6704 mouse_button:
6705 mouse = MO_BUTTON(event.button.which, event.button.button);
6706 manage_combos(megad, pressed, RCBM, mouse);
6707 if (calibrating) {
6708 if (pressed)
6709 manage_calibration(RCBM, mouse);
6710 break;
6712 if (events != STARTED)
6713 break;
6714 for (struct ctl* ctl = control; (ctl->rc != NULL); ++ctl) {
6715 if ((*ctl->rc)[RCBM] != mouse)
6716 continue;
6717 ctl->pressed = pressed;
6718 ctl->coord = true;
6719 ctl->x = event.button.x;
6720 ctl->y = event.button.y;
6721 if (pressed == false) {
6722 if ((ctl->release != NULL) &&
6723 (ctl->release(*ctl, megad) == 0))
6724 return 0;
6726 else {
6727 assert(ctl->press != NULL);
6728 if (ctl->press(*ctl, megad) == 0)
6729 return 0;
6732 if (manage_bindings(megad, pressed, RCBM, mouse) == 0)
6733 return 0;
6734 break;
6735 case SDL_VIDEORESIZE:
6736 switch (screen_init(event.resize.w, event.resize.h)) {
6737 case 0:
6738 pd_message("Video resized to %ux%u.",
6739 screen.surface->w,
6740 screen.surface->h);
6741 break;
6742 case -1:
6743 pd_message("Failed to resize video to %ux%u.",
6744 event.resize.w,
6745 event.resize.h);
6746 break;
6747 default:
6748 fprintf(stderr,
6749 "sdl: fatal error while trying to change screen"
6750 " resolution.\n");
6751 return 0;
6753 break;
6754 case SDL_QUIT:
6755 // We've been politely asked to exit, so let's leave
6756 return 0;
6757 default:
6758 break;
6760 goto next_event;
6763 static size_t pd_message_write(const char *msg, size_t len, unsigned int mark)
6765 uint8_t *buf = (screen.buf.u8 +
6766 (screen.pitch * (screen.height - screen.info_height)));
6767 size_t ret = 0;
6769 screen_lock();
6770 // Clear text area.
6771 memset(buf, 0x00, (screen.pitch * screen.info_height));
6772 // Write message.
6773 if (len != 0)
6774 ret = font_text(buf, screen.width, screen.info_height,
6775 screen.Bpp, screen.pitch, msg, len,
6776 mark, FONT_TYPE_AUTO);
6777 screen_unlock();
6778 return ret;
6781 static size_t pd_message_display(const char *msg, size_t len,
6782 unsigned int mark, bool update)
6784 size_t ret = pd_message_write(msg, len, mark);
6786 if (update)
6787 screen_update();
6788 if (len == 0)
6789 info.displayed = 0;
6790 else {
6791 info.displayed = 1;
6792 info.since = pd_usecs();
6794 return ret;
6798 * Process status bar message.
6800 static void pd_message_process(void)
6802 size_t len = info.length;
6803 size_t n;
6804 size_t r;
6806 if (len == 0) {
6807 pd_clear_message();
6808 return;
6810 for (n = 0; (n < len); ++n)
6811 if (info.message[n] == '\n') {
6812 len = (n + 1);
6813 break;
6815 r = pd_message_display(info.message, n, ~0u, false);
6816 if (r < n)
6817 len = r;
6818 memmove(info.message, &(info.message[len]), (info.length - len));
6819 info.length -= len;
6823 * Postpone a message.
6825 static void pd_message_postpone(const char *msg)
6827 strncpy(&info.message[info.length], msg,
6828 (sizeof(info.message) - info.length));
6829 info.length = strlen(info.message);
6830 info.displayed = 1;
6834 * Write a message to the status bar.
6836 void pd_message(const char *msg, ...)
6838 va_list vl;
6840 va_start(vl, msg);
6841 vsnprintf(info.message, sizeof(info.message), msg, vl);
6842 va_end(vl);
6843 info.length = strlen(info.message);
6844 pd_message_process();
6847 void pd_clear_message()
6849 pd_message_display(NULL, 0, ~0u, false);
6852 void pd_show_carthead(md& megad)
6854 struct {
6855 const char *p;
6856 const char *s;
6857 size_t len;
6858 } data[] = {
6859 #define CE(i, s) { i, s, sizeof(s) }
6860 CE("System", megad.cart_head.system_name),
6861 CE("Copyright", megad.cart_head.copyright),
6862 CE("Domestic name", megad.cart_head.domestic_name),
6863 CE("Overseas name", megad.cart_head.overseas_name),
6864 CE("Product number", megad.cart_head.product_no),
6865 CE("Memo", megad.cart_head.memo),
6866 CE("Countries", megad.cart_head.countries)
6868 size_t i;
6870 pd_message_postpone("\n");
6871 for (i = 0; (i < (sizeof(data) / sizeof(data[0]))); ++i) {
6872 char buf[256];
6873 size_t j, k;
6875 k = (size_t)snprintf(buf, sizeof(buf), "%s: ", data[i].p);
6876 if (k >= (sizeof(buf) - 1))
6877 continue;
6878 // Filter out extra spaces.
6879 for (j = 0; (j < data[i].len); ++j)
6880 if (isgraph(data[i].s[j]))
6881 break;
6882 if (j == data[i].len)
6883 continue;
6884 while ((j < data[i].len) && (k < (sizeof(buf) - 2))) {
6885 if (isgraph(data[i].s[j])) {
6886 buf[(k++)] = data[i].s[j];
6887 ++j;
6888 continue;
6890 buf[(k++)] = ' ';
6891 while ((j < data[i].len) && (!isgraph(data[i].s[j])))
6892 ++j;
6894 if (buf[(k - 1)] == ' ')
6895 --k;
6896 buf[k] = '\n';
6897 buf[(k + 1)] = '\0';
6898 pd_message_postpone(buf);
6902 /* Clean up this awful mess :) */
6903 void pd_quit()
6905 size_t i;
6907 #ifdef WITH_THREADS
6908 screen_update_thread_stop();
6909 #endif
6910 if (mdscr.data) {
6911 free((void*)mdscr.data);
6912 mdscr.data = NULL;
6914 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
6915 pd_sound_deinit();
6916 if (mdpal)
6917 mdpal = NULL;
6918 #ifdef WITH_OPENGL
6919 release_texture(screen.texture);
6920 #endif
6921 free(filters_stack_data_buf[0].u8);
6922 free(filters_stack_data_buf[1].u8);
6923 assert(filters_stack_size <= elemof(filters_stack));
6924 assert(filters_stack_data[0].data == NULL);
6925 filters_stack_default = false;
6926 for (i = 0; (i != filters_stack_size); ++i) {
6927 free(filters_stack_data[i + 1].data);
6928 filters_stack_data[i + 1].data = NULL;
6930 filters_stack_size = 0;
6931 SDL_Quit();