21 #include <SDL_audio.h>
24 # include <SDL_opengl.h>
28 #include <SDL_thread.h>
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.
77 /// Framebuffer 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
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
&);
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
115 struct texture texture
; ///< OpenGL texture data
116 unsigned int want_opengl
:1; ///< want OpenGL
117 unsigned int is_opengl
:1; ///< OpenGL enabled
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
126 SDL_Color color
[64]; ///< SDL colors for 8bpp modes
129 static struct screen screen
;
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)
138 320, ///< width is always 320
139 NTSC_VBLANK
, ///< NTSC height by default
141 0, ///< NTSC is enabled
146 * Call this before accessing screen.buf.
147 * No syscalls allowed before screen_unlock().
149 static int screen_lock()
152 if (screen
.is_thread
) {
153 assert(screen
.lock
!= NULL
);
154 SDL_LockMutex(screen
.lock
);
158 if (screen
.is_opengl
)
161 if (SDL_MUSTLOCK(screen
.surface
) == 0)
163 return SDL_LockSurface(screen
.surface
);
167 * Call this after accessing screen.buf.
169 static void screen_unlock()
172 if (screen
.is_thread
) {
173 assert(screen
.lock
!= NULL
);
174 SDL_UnlockMutex(screen
.lock
);
178 if (screen
.is_opengl
)
181 if (SDL_MUSTLOCK(screen
.surface
) == 0)
183 SDL_UnlockSurface(screen
.surface
);
187 * Do not call this directly, use screen_update() instead.
189 static void screen_update_once()
192 if (screen
.is_opengl
) {
193 update_texture(screen
.texture
);
197 SDL_Flip(screen
.surface
);
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
);
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
);
223 if (screen
.is_opengl
) {
224 DEBUG(("this is not supported when OpenGL is enabled"));
228 if ((screen
.lock
= SDL_CreateMutex()) == NULL
) {
229 DEBUG(("unable to create lock"));
233 if ((screen
.cond
= SDL_CreateCond()) == NULL
) {
234 DEBUG(("unable to create condition variable"));
237 screen
.thread
= SDL_CreateThread(screen_update_thread
, NULL
);
238 if (screen
.thread
== NULL
) {
239 DEBUG(("unable to start thread"));
242 screen
.is_thread
= 1;
243 DEBUG(("thread started"));
246 if (screen
.cond
!= NULL
) {
247 SDL_DestroyCond(screen
.cond
);
250 if (screen
.lock
!= NULL
) {
251 SDL_DestroyMutex(screen
.lock
);
256 static void screen_update_thread_stop()
258 if (!screen
.is_thread
) {
259 assert(screen
.thread
== NULL
);
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
);
270 SDL_DestroyMutex(screen
.lock
);
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()
284 if (screen
.is_thread
)
285 SDL_CondSignal(screen
.cond
);
287 #endif // WITH_THREADS
288 screen_update_once();
294 static void screen_clear()
296 if ((screen
.buf
.u8
== NULL
) || (screen_lock()))
298 memset(screen
.buf
.u8
, 0, (screen
.pitch
* screen
.height
));
302 // Bad hack- extern slot etc. from main.cpp so we can save/load states
304 void md_save(md
&megad
);
305 void md_load(md
&megad
);
307 // Define externed variables
309 unsigned char *mdpal
= NULL
;
311 const char *pd_options
=
317 static void mdscr_splash();
319 /// Circular buffer and related functions.
321 size_t i
; ///< data start index
322 size_t s
; ///< data size
323 size_t size
; ///< buffer size
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
)
342 if (size
> cbuf
->size
) {
343 src
+= (size
- cbuf
->size
);
346 k
= (cbuf
->size
- cbuf
->s
);
347 j
= ((cbuf
->i
+ cbuf
->s
) % cbuf
->size
);
349 cbuf
->i
= ((cbuf
->i
+ (size
- k
)) % cbuf
->size
);
350 cbuf
->s
= cbuf
->size
;
354 k
= (cbuf
->size
- j
);
356 memcpy(&cbuf
->data
.u8
[j
], src
, size
);
359 memcpy(&cbuf
->data
.u8
[j
], src
, k
);
360 memcpy(&cbuf
->data
.u8
[0], &src
[k
], (size
- k
));
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
)
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
));
383 memcpy(&dst
[0], &cbuf
->data
.u8
[(cbuf
->i
)], size
);
384 cbuf
->i
= ((cbuf
->i
+ size
) % cbuf
->size
);
391 unsigned int rate
; ///< samples rate
392 unsigned int samples
; ///< number of samples required by the callback
393 cbuf_t cbuf
; ///< circular buffer
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
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
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
{
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**,
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**,
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**,
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**);
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**,
450 static char* prompt_cmpl_vgmdump(class md
&, unsigned int, const char**,
452 static int prompt_cmd_vgmdump(class md
&, unsigned int, const char**);
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
},
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
},
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
},
481 { "vgmdump", prompt_cmd_vgmdump
, prompt_cmpl_vgmdump
},
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.
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);
529 assert(pd_freeze
== true);
535 assert(pd_freeze
== false);
540 * Elapsed time in microseconds.
541 * @return Microseconds.
543 unsigned long pd_usecs(void)
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.
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
)
570 extern void ram_save(class md
&);
571 extern void ram_load(class md
&);
576 s
= backslashify((const uint8_t *)av
[1], strlen(av
[1]), 0, NULL
);
586 if (md
.load(av
[1])) {
588 pd_message("Unable to load \"%s\"", s
);
590 return (CMD_FAIL
| CMD_MSG
);
592 pd_message("Loaded \"%s\"", s
);
594 if (dgen_show_carthead
)
595 pd_show_carthead(md
);
596 // Initialize like main() does.
600 uint8_t c
= md
.region_guess();
604 md::region_info(c
, &pal
, &hz
, 0, 0, 0);
605 if ((hz
!= dgen_hz
) || (pal
!= dgen_pal
) || (c
!= md
.region
)) {
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
);
613 long rate
= dgen_soundrate
;
614 unsigned int samples
;
617 samples
= (dgen_soundsegs
* (rate
/ dgen_hz
));
618 pd_sound_init(rate
, samples
);
631 return (CMD_OK
| CMD_MSG
);
634 static void rehash_prompt_complete_common()
640 if ((prompt
.complete
== NULL
) || (prompt
.complete
[0] == NULL
))
642 common
= strlen(prompt
.complete
[0]);
643 for (i
= 1; (prompt
.complete
[i
] != NULL
); ++i
) {
646 tmp
= strcommon(prompt
.complete
[i
], prompt
.complete
[(i
- 1)]);
652 prompt
.common
= common
;
655 static char* prompt_cmpl_load(class md
& md
, unsigned int ac
, const char** av
,
664 if ((ac
== 1) || (len
== ~0u) || (av
[(ac
- 1)] == NULL
)) {
669 prefix
= av
[(ac
- 1)];
670 if (prompt
.complete
== NULL
) {
673 prompt
.complete
= complete_path(prefix
, len
,
675 if (prompt
.complete
== NULL
)
677 rehash_prompt_complete_common();
681 for (i
= 0; (prompt
.complete
[i
] != NULL
); ++i
) {
686 if (prompt
.complete
[i
] == NULL
) {
687 if (prompt
.skip
!= 0) {
694 return strdup(prompt
.complete
[i
]);
697 static int prompt_cmd_unload(class md
& md
, unsigned int, const char**)
700 extern void ram_save(class md
&);
702 info
.length
= 0; // clear postponed messages
703 pd_message("No cartridge.");
710 return (CMD_FAIL
| CMD_MSG
);
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
)
724 if ((ac
== 1) || (len
== ~0u) || (av
[(ac
- 1)] == NULL
)) {
729 prefix
= av
[(ac
- 1)];
730 if (prompt
.complete
== NULL
) {
733 prompt
.complete
= complete_path(prefix
, len
, "config");
734 if (prompt
.complete
== NULL
)
736 rehash_prompt_complete_common();
740 for (i
= 0; (prompt
.complete
[i
] != NULL
); ++i
) {
745 if (prompt
.complete
[i
] == NULL
) {
746 if (prompt
.skip
!= 0) {
753 return strdup(prompt
.complete
[i
]);
756 static int prompt_rehash_rc_field(const struct rc_field
*, md
&);
759 * Prompt "config_load" command handler.
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
,
774 s
= backslashify((const uint8_t *)av
[1], strlen(av
[1]), 0, NULL
);
777 f
= dgen_fopen("config", av
[1], (DGEN_READ
| DGEN_CURRENT
));
779 pd_message("Cannot load configuration \"%s\": %s.",
782 return (CMD_FAIL
| CMD_MSG
);
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
);
790 return (CMD_OK
| CMD_MSG
);
794 * Prompt "config_save" command handler.
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
,
809 s
= backslashify((const uint8_t *)av
[1], strlen(av
[1]), 0, NULL
);
812 f
= dgen_fopen("config", av
[1], (DGEN_WRITE
| DGEN_TEXT
));
814 pd_message("Cannot save configuration \"%s\": %s",
817 return (CMD_FAIL
| CMD_MSG
);
821 pd_message("Saved configuration \"%s\"", s
);
823 return (CMD_OK
| CMD_MSG
);
826 static int prompt_cmd_reset(class md
& md
, unsigned int, const char**)
832 static int prompt_cmd_unbind(class md
&, unsigned int ac
, const char** av
)
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
))) {
856 static char* prompt_cmpl_unbind(class md
&, unsigned int ac
,
857 const char** av
, unsigned int len
)
859 const struct rc_binding
*rcb
;
864 if ((ac
== 1) || (len
== ~0u) || (av
[(ac
- 1)] == NULL
)) {
869 prefix
= av
[(ac
- 1)];
872 for (rcb
= rc_binding_head
.next
;
873 (rcb
!= &rc_binding_head
);
875 if (strncasecmp(prefix
, rcb
->rc
, len
))
881 if (rcb
== &rc_binding_head
) {
882 if (prompt
.skip
!= 0) {
889 return strdup(rcb
->rc
);
894 static char* prompt_cmpl_vgmdump(class md
& md
, unsigned int ac
,
895 const char** av
, unsigned int len
)
903 if ((ac
== 1) || (len
== ~0u) || (av
[(ac
- 1)] == NULL
)) {
908 prefix
= av
[(ac
- 1)];
909 if (prompt
.complete
== NULL
) {
912 prompt
.complete
= complete_path(prefix
, len
, "vgm");
913 if (prompt
.complete
== NULL
)
915 rehash_prompt_complete_common();
919 for (i
= 0; (prompt
.complete
[i
] != NULL
); ++i
) {
924 if (prompt
.complete
[i
] == NULL
) {
925 if (prompt
.skip
!= 0) {
932 return strdup(prompt
.complete
[i
]);
935 static int prompt_cmd_vgmdump(class md
& md
, unsigned int ac
, const char** av
)
941 if (!strcasecmp(av
[1], "stop")) {
942 if (md
.vgm_dump
== false)
943 pd_message("VGM dumping already stopped.");
946 pd_message("Stopped VGM dumping.");
948 return (CMD_OK
| CMD_MSG
);
950 if (strcasecmp(av
[1], "start"))
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
);
959 if (md
.vgm_dump_start(av
[2])) {
960 pd_message("Cannot dump VGM to \"%s\": %s",
963 return (CMD_FAIL
| CMD_MSG
);
965 pd_message("Started VGM dumping to \"%s\"", s
);
967 return (CMD_OK
| CMD_MSG
);
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
);
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
;
997 static filter_func_t filter_scale2x
;
1000 static filter_func_t filter_hqx
;
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();
1011 static const struct filter filters_available
[] = {
1012 { "stretch", filter_stretch
, false, false, true },
1013 { "scale", filter_scale
, false, false, true },
1015 { "scale2x", filter_scale2x
, false, false, true },
1018 { "hqx", filter_hqx
, false, false, true },
1020 { "none", filter_off
, true, false, true },
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 },
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
)
1046 for (i
= 0; (i
!= elemof(filters_available
)); ++i
)
1047 if (!strcasecmp(name
, filters_available
[i
].name
))
1048 return &filters_available
[i
];
1053 * Update filters data, reallocate extra buffers if necessary.
1055 static void filters_stack_update()
1058 const struct filter
*f
;
1059 struct filter_data
*fd
;
1060 unsigned int buffers
;
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) },
1068 (unsigned int)mdscr
.pitch
,
1073 struct filter_data out_fd
= {
1076 (screen
.height
- screen
.info_height
),
1082 struct filter_data
*prev_fd
;
1084 DEBUG(("updating filters data"));
1086 assert(filters_stack_size
<= elemof(filters_stack
));
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
)
1097 f
= filters_stack
[i
];
1099 if ((f
->safe
== false) && (i
!= (filters_stack_size
- 1)))
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.
1107 assert(filters_stack_size
== 0);
1108 filters_stack
[0] = &filters_available
[0];
1109 ++filters_stack_size
;
1110 filters_stack_default
= true;
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;
1122 // Check if extra buffers are required.
1127 // Remove unnecessary buffer.
1130 filters_stack_data_buf
[1].u8
= NULL
;
1132 DEBUG(("requiring %u extra buffer(s)", buffers
));
1134 for (i
= 0; (i
!= buffers
); ++i
) {
1135 size_t size
= (screen
.pitch
* screen
.height
);
1137 DEBUG(("temporary buffer %u size: %zu", i
, size
));
1139 (uint8_t *)realloc((void *)buf
[i
].u8
, size
);
1141 assert(buf
[i
].u8
== NULL
);
1142 DEBUG(("freed zero-sized buffer"));
1143 filters_stack_data_buf
[i
].u8
= NULL
;
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
;
1152 (i
< filters_stack_size
);
1154 if (filters_stack
[i
]->safe
== true)
1156 --filters_stack_size
;
1157 memmove(&filters_stack
[i
],
1158 &filters_stack
[i
+ 1],
1159 (sizeof(filters_stack
[i
]) *
1160 (filters_stack_size
- i
)));
1165 filters_stack_data_buf
[i
].u8
= buf
[i
].u8
;
1169 // No extra buffer required, deallocate them.
1170 DEBUG(("removing temporary buffers"));
1171 for (i
= 0; (i
!= elemof(buf
)); ++i
) {
1174 filters_stack_data_buf
[i
].u8
= NULL
;
1177 // Update I/O buffers.
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
)
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.
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
;
1199 fd
->updated
= false;
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
));
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
)))
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
));
1240 (filters_stack_size
== elemof(filters_stack
)))
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.
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
)
1262 assert(filters_stack_size
<= elemof(filters_stack
));
1265 DEBUG(("%s", f
->name
));
1266 for (i
= 0; (i
!= filters_stack_size
); ++i
)
1267 if (filters_stack
[i
] == f
)
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
);
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]));
1293 filters_stack_update();
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
)
1307 --filters_stack_size
;
1308 DEBUG(("%s", filters_stack
[index
]->name
));
1309 free(filters_stack_data
[index
+ 1].data
);
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]));
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
)
1331 assert(filters_stack_size
<= elemof(filters_stack
));
1334 DEBUG(("%s", f
->name
));
1335 for (i
= 0; (i
< filters_stack_size
); ++i
) {
1336 if (filters_stack
[i
] != f
)
1338 --filters_stack_size
;
1339 DEBUG(("%s", filters_stack
[i
]->name
));
1340 free(filters_stack_data
[i
+ 1].data
);
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]));
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
)));
1353 filters_stack_update();
1359 * Remove all occurences of CTV filters from the stack.
1361 static void filters_pluck_ctv()
1365 assert(filters_stack_size
<= elemof(filters_stack
));
1366 for (i
= 0; (i
< filters_stack_size
); ++i
) {
1367 if (filters_stack
[i
]->ctv
== false)
1369 --filters_stack_size
;
1370 DEBUG(("%s", filters_stack
[i
]->name
));
1371 free(filters_stack_data
[i
+ 1].data
);
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]));
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
)));
1384 filters_stack_update();
1392 * Empty filters stack.
1394 static void filters_empty()
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;
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
;
1408 filters_stack_update();
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
)];
1428 unsigned int height
;
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);
1441 width
= screen
.width
;
1442 height
= screen
.height
;
1443 pitch
= screen
.pitch
;
1453 pd_message("Screenshots unsupported in %d bpp.", bpp
);
1456 // Make take a long time, let the main loop know about it.
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
));
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
);
1468 pd_message("Can't open %s.", name
);
1471 fseek(fp
, 0, SEEK_END
);
1477 if (((off_t
)pos
== (off_t
)-1) || ((off_t
)pos
!= (off_t
)0)) {
1479 n
= ((n
+ 1) % 1000000);
1482 // Allocate line buffer.
1483 if ((out
= (uint8_t (*)[3])malloc(sizeof(*out
) * width
)) == NULL
)
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
))
1501 h2le16(width
), // width
1502 h2le16(height
) // height
1505 if (!fwrite(tmp
, sizeof(tmp
), 1, fp
))
1510 24, // always output 24 bits per pixel
1511 (1 << 5) // top-left origin
1514 if (!fwrite(tmp
, sizeof(tmp
), 1, fp
))
1523 for (y
= 0; (y
< height
); ++y
) {
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);
1534 if (!fwrite(out
, (sizeof(*out
) * width
), 1, fp
))
1540 for (y
= 0; (y
< height
); ++y
) {
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);
1551 if (!fwrite(out
, (sizeof(*out
) * width
), 1, fp
))
1557 for (y
= 0; (y
< height
); ++y
) {
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];
1567 memcpy(out
, line
.u24
, (sizeof(*out
) * width
));
1570 if (!fwrite(out
, (sizeof(*out
) * width
), 1, fp
))
1576 for (y
= 0; (y
< height
); ++y
) {
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);
1585 memcpy(&(out
[x
]), &(line
.u32
[x
]), 3);
1589 if (!fwrite(out
, (sizeof(*out
) * width
), 1, fp
))
1595 pd_message("Screenshot written to %s.", name
);
1600 pd_message("Error while generating screenshot %s.", name
);
1612 " -g (1|0) Enable/disable OpenGL.\n"
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
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
;
1639 * Handle the switches.
1640 * @param c Switch's value.
1642 void pd_option(char c
, const char *)
1649 dgen_opengl
= atoi(optarg
);
1653 dgen_fullscreen
= 1;
1656 if ((xs
= atoi(optarg
)) <= 0)
1661 if ((ys
= atoi(optarg
)) <= 0)
1666 if ((xs
= atoi(optarg
)) <= 0)
1672 if ((sscanf(optarg
, " %d x %d ", &xs
, &ys
) != 2) ||
1673 (xs
< 0) || (ys
< 0))
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
1687 #define TEXTURE_16_TYPE GL_UNSIGNED_SHORT_5_6_5
1688 #define TEXTURE_32_TYPE GL_UNSIGNED_BYTE
1691 static void texture_init_id(struct texture
& texture
)
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
,
1710 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGB
,
1711 texture
.width
, texture
.height
,
1712 0, GL_BGRA
, TEXTURE_32_TYPE
,
1716 static void texture_init_dlist(struct texture
& texture
)
1718 glNewList(texture
.dlist
, GL_COMPILE
);
1719 glMatrixMode(GL_MODELVIEW
);
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
);
1730 glVertex2i(0, texture
.height
); // lower left
1732 glVertex2i(0, 0); // upper left
1734 glVertex2i(texture
.width
, 0); // upper right
1736 glVertex2i(texture
.width
, texture
.height
); // lower right
1739 glDisable(GL_TEXTURE_2D
);
1745 * Round a value up to nearest power of two.
1747 * @return Rounded value.
1749 static uint32_t roundup2(uint32_t 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);
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
;
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
;
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
) {
1809 // Aspect ratio won't be kept.
1815 w
= vis_width
= screen
->width
;
1816 h
= vis_height
= screen
->height
;
1818 if (screen
->width
> w
)
1819 x
= ((screen
->width
- w
) / 2);
1822 if (screen
->height
> h
)
1823 y
= ((screen
->height
- h
) / 2);
1826 DEBUG(("initializing for width=%u height=%u", vis_width
, vis_height
));
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
;
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))
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
)
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)
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".
1878 DEBUG(("texture initialization OK"));
1881 release_texture(texture
);
1882 DEBUG(("texture initialization failed"));
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
,
1895 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0,
1896 texture
.vis_width
, texture
.vis_height
,
1897 GL_BGRA
, TEXTURE_32_TYPE
,
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
)
1914 unsigned int height
;
1918 // Check if copying is necessary.
1919 if (in
->buf
.u8
== out
->buf
.u8
)
1921 // Copy line by line and center.
1922 if (in
->height
> out
->height
)
1923 height
= out
->height
;
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
;
1971 for (y
= 0; (y
!= height
); ++y
) {
1976 for (x
= 0; (x
!= width
); ++x
) {
1977 uintX_t tmp
= src
[x
];
1979 for (i
= 0; (i
!= x_scale
); ++i
)
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
));
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
;
2008 for (y
= 0; (y
!= height
); ++y
) {
2009 uint24_t
*out
= dst
;
2013 for (x
= 0; (x
!= width
); ++x
) {
2016 u24cpy(&tmp
, &src
[x
]);
2017 for (i
= 0; (i
!= x_scale
); ++i
)
2018 u24cpy((out
++), &tmp
);
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
));
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 {
2041 filter_func_t
*func
;
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
;
2050 unsigned int height
;
2053 unsigned int x_scale
;
2054 unsigned int y_scale
;
2055 filter_func_t
*filter
;
2058 if (out
->failed
== true) {
2060 filter_off(in
, out
);
2063 if (out
->updated
== true) {
2064 data
= (struct filter_scale_data
*)out
->data
;
2065 filter
= data
->filter
;
2067 // Feed this to the basic scaler.
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
)
2077 while ((height
= (in
->height
* y_scale
)) > out
->height
)
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
));
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
));
2091 // Find a suitable filter.
2092 for (i
= 0; (i
!= elemof(scale_mode
)); ++i
)
2093 if (scale_mode
[i
].Bpp
== screen
.Bpp
)
2095 if (i
== elemof(scale_mode
)) {
2096 DEBUG(("%u Bpp depth is not supported", screen
.Bpp
));
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
));
2104 DEBUG(("allocation failure"));
2108 filter
= scale_mode
[i
].func
;
2109 data
->filter
= filter
;
2110 data
->x_scale
= x_scale
;
2111 data
->y_scale
= y_scale
;
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
);
2118 out
->height
= height
;
2119 out
->data
= (void *)data
;
2120 out
->updated
= true;
2124 struct filter_stretch_data
{
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
;
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
];
2158 for (src_x
= 0, dst_x
= 0; (src_x
!= src_w
); ++src_x
) {
2159 uint8_t h_repeat
= h_table
[src_x
];
2164 dst
[dst_x
++] = src
[src_x
];
2167 while (--v_repeat
) {
2168 memcpy(dst
, (dst
- dst_pitch
), (dst_w
* sizeof(*dst
)));
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
;
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
];
2202 for (src_x
= 0, dst_x
= 0; (src_x
!= src_w
); ++src_x
) {
2203 uint8_t h_repeat
= h_table
[src_x
];
2208 u24cpy(&dst
[dst_x
++], &src
[src_x
]);
2211 while (--v_repeat
) {
2212 memcpy(dst
, (dst
- dst_pitch
), (dst_w
* sizeof(*dst
)));
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 {
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
;
2241 unsigned int h_ratio
;
2242 unsigned int v_ratio
;
2247 filter_func_t
*filter
;
2250 if (out
->failed
== true) {
2252 filter_off(in
, out
);
2255 if (out
->updated
== true) {
2256 data
= (struct filter_stretch_data
*)out
->data
;
2257 filter
= data
->filter
;
2262 // Initialize filter.
2263 assert(out
->data
== NULL
);
2265 dst_h
= out
->height
;
2268 if ((src_h
== 0) || (src_w
== 0)) {
2269 DEBUG(("invalid input size: %ux%u", src_h
, src_w
));
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
));
2281 // Find a suitable filter.
2282 for (i
= 0; (i
!= elemof(stretch_mode
)); ++i
)
2283 if (stretch_mode
[i
].Bpp
== screen
.Bpp
)
2285 if (i
== elemof(stretch_mode
)) {
2286 DEBUG(("%u Bpp depth is not supported", screen
.Bpp
));
2290 filter
= stretch_mode
[i
].func
;
2291 // Fix output if original aspect ratio must be kept.
2293 unsigned int w
= ((dst_h
* src_w
) / src_h
);
2294 unsigned int h
= ((dst_w
* src_h
) / src_w
);
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
);
2315 DEBUG(("allocation failure"));
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
);
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
);
2333 ++data
->v_table
[src_y
];
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
);
2341 out
->height
= dst_h
;
2342 out
->data
= (void *)data
;
2343 out
->updated
= true;
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 {
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;
2377 unsigned int height
;
2380 unsigned int x_scale
;
2381 unsigned int y_scale
;
2385 if (out
->failed
== true) {
2387 filter_off(in
, out
);
2390 if (out
->updated
== true) {
2391 hqx
= *(hqx_func_t
**)out
->data
;
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
);
2399 // Initialize filter.
2400 assert(out
->data
== NULL
);
2401 x_scale
= screen
.x_scale
;
2402 y_scale
= screen
.y_scale
;
2404 while ((width
= (in
->width
* x_scale
)) > out
->width
)
2406 while ((height
= (in
->height
* y_scale
)) > out
->height
)
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
));
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
))
2420 if (i
== elemof(hqx_mode
)) {
2421 // Nothing matches, find something that fits.
2422 DEBUG(("%ux%u @%u Bpp scale factor not supported, trying"
2424 x_scale
, y_scale
, screen
.Bpp
));
2426 if (x_scale
> y_scale
)
2428 else if (y_scale
> x_scale
)
2430 assert(x_scale
== y_scale
);
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
;
2442 DEBUG(("failed to use %ux%u @%u Bpp scale factor",
2443 x_scale
, y_scale
, screen
.Bpp
));
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"));
2456 *(hqx_func_t
**)out
->data
= hqx
;
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
);
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...");
2470 pd_message_cursor(~0u, "");
2471 hqx_initialized
= true;
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
;
2492 } scale2x_mode
[] = {
2500 unsigned int height
;
2503 unsigned int x_scale
;
2504 unsigned int y_scale
;
2508 if (out
->failed
== true) {
2510 filter_off(in
, out
);
2513 if (out
->updated
== true) {
2514 mode
= *(unsigned int *)out
->data
;
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
);
2521 // Initialize filter.
2522 assert(out
->data
== NULL
);
2523 x_scale
= screen
.x_scale
;
2524 y_scale
= screen
.y_scale
;
2526 while ((width
= (in
->width
* x_scale
)) > out
->width
)
2528 while ((height
= (in
->height
* y_scale
)) > out
->height
)
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
));
2536 // Check whether depth is supported by filter.
2537 if ((screen
.Bpp
!= 4) && (screen
.Bpp
!= 2)) {
2538 DEBUG(("unsupported depth %u", screen
.bpp
));
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
))
2547 if (i
== elemof(scale2x_mode
)) {
2548 // Nothing matches, find something that fits.
2549 DEBUG(("%ux%u scale factor not supported, trying another",
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
;
2561 DEBUG(("failed to use %ux%u scale factor", x_scale
, y_scale
));
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"));
2573 *(unsigned int *)out
->data
= mode
;
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
);
2580 out
->height
= height
;
2581 out
->updated
= true;
2585 #endif // WITH_SCALE2X
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
;
2600 for (y
= 0; (y
< ysize
); ++y
) {
2601 uint32_t old
= *in_buf
.u32
;
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
;
2628 for (y
= 0; (y
< ysize
); ++y
) {
2632 u24cpy(&old
, in_buf
.u24
);
2633 for (x
= 0; (x
< xsize
); ++x
) {
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);
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
;
2657 if (in_buf
.u16
== out_buf
.u16
) {
2658 for (y
= 0; (y
< ysize
); ++y
) {
2660 blur_bitmap_16((uint8_t *)out_buf
.u16
, (xsize
- 1));
2661 out_buf
.u8
+= out
->pitch
;
2666 for (y
= 0; (y
< ysize
); ++y
) {
2667 uint16_t old
= *in_buf
.u16
;
2670 for (x
= 0; (x
< xsize
); ++x
) {
2671 uint16_t tmp
= in_buf
.u16
[x
];
2673 tmp
= (((((tmp
& 0xf81f) +
2674 (old
& 0xf81f)) >> 1) & 0xf81f) |
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
;
2695 if (in_buf
.u15
== out_buf
.u15
) {
2696 for (y
= 0; (y
< ysize
); ++y
) {
2698 blur_bitmap_15((uint8_t *)out_buf
.u15
, (xsize
- 1));
2699 out_buf
.u8
+= out
->pitch
;
2704 for (y
= 0; (y
< ysize
); ++y
) {
2705 uint16_t old
= *in_buf
.u15
;
2708 for (x
= 0; (x
< xsize
); ++x
) {
2709 uint16_t tmp
= in_buf
.u15
[x
];
2711 tmp
= (((((tmp
& 0x7c1f) +
2712 (old
& 0x7c1f)) >> 1) & 0x7c1f) |
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 {
2728 filter_func_t
*filter
;
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) {
2739 filter_off(in
, out
);
2742 if (out
->updated
== false) {
2745 for (i
= 0; (i
!= elemof(blur_mode
)); ++i
)
2746 if (blur_mode
[i
].bpp
== screen
.bpp
)
2748 if (i
== elemof(blur_mode
)) {
2749 DEBUG(("%u bpp depth is not supported", screen
.bpp
));
2753 blur
= blur_mode
[i
].filter
;
2754 out
->data
= malloc(sizeof(filter
));
2755 if (out
->data
== NULL
) {
2756 DEBUG(("allocation failure"));
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;
2774 blur
= *(filter_func_t
**)out
->data
;
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
);
2796 for (y
= frame
; (y
< ysize
); y
+= 2) {
2797 for (x
= 0; (x
< xsize
); ++x
)
2799 ((in_buf
.u32
[x
] >> 1) & 0x7f7f7f7f);
2800 in_buf
.u8
+= (in
->pitch
* 2);
2801 out_buf
.u8
+= (out
->pitch
* 2);
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);
2816 for (y
= frame
; (y
< ysize
); y
+= 2) {
2818 if (in_buf
.u16
== out_buf
.u16
) {
2819 // Scanline, by Phil
2820 test_ctv((uint8_t *)out_buf
.u16
, xsize
);
2824 for (x
= 0; (x
< xsize
); ++x
)
2826 ((in_buf
.u16
[x
] >> 1) & 0x7bef);
2827 in_buf
.u8
+= (in
->pitch
* 2);
2828 out_buf
.u8
+= (out
->pitch
* 2);
2832 for (y
= frame
; (y
< ysize
); y
+= 2) {
2834 if (in_buf
.u15
== out_buf
.u15
) {
2835 // Scanline, by Phil
2836 test_ctv((uint8_t *)out_buf
.u16
, xsize
);
2840 for (x
= 0; (x
< xsize
); ++x
)
2842 ((in_buf
.u15
[x
] >> 1) & 0x3def);
2843 in_buf
.u8
+= (in
->pitch
* 2);
2844 out_buf
.u8
+= (out
->pitch
* 2);
2850 static void filter_scanline(const struct filter_data
*in
,
2851 struct filter_data
*out
)
2853 if (out
->failed
== true) {
2855 filter_off(in
, out
);
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
));
2867 out
->data
= malloc(sizeof(unsigned int [2]));
2868 if (out
->data
== NULL
) {
2869 DEBUG(("allocation failure"));
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) {
2895 filter_off(in
, out
);
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
));
2907 out
->data
= malloc(sizeof(unsigned int [2]));
2908 if (out
->data
== NULL
) {
2909 DEBUG(("allocation failure"));
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
)
2940 if (out
->failed
== true) {
2942 filter_off(in
, out
);
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
));
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;
2967 ysize
= out
->height
;
2969 switch (screen
.Bpp
) {
2974 for (y
= 0; (y
< ysize
); ++y
) {
2975 for (x
= 0; (x
< xsize
); ++x
) {
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
;
2993 for (y
= 0; (y
< ysize
); ++y
) {
2994 for (x
= 0; (x
< xsize
); ++x
) {
3001 u24cpy(&out_buf
.u24
[x
], &tmp
);
3003 in_buf
.u8
+= in
->pitch
;
3004 out_buf
.u8
+= out
->pitch
;
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
;
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
, ...)
3050 size_t len
= sizeof(filter_text_str
);
3053 assert(filter_text_str
[(len
- 1)] == '\0');
3054 off
= strlen(filter_text_str
);
3059 vsnprintf(&filter_text_str
[off
], len
, fmt
, 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
;
3081 enum { LEFT
, CENTER
, RIGHT
} justify
= LEFT
;
3083 enum font_type type
;
3085 unsigned int height
;
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
;
3098 assert(filter_text_str
[(sizeof(filter_text_str
) - 1)] == '\0');
3103 if ((*next
== '\0') || (*next
== '\n')) {
3105 if (flush
== false) {
3107 assert(line_width
<= xsize
);
3113 line_off
= ((xsize
- line_width
) / 2);
3116 line_off
= (xsize
- line_width
);
3121 (buf_pitch
* line_height
));
3125 else if (*next
== '\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
)
3144 else if (*next
== *FILTER_TEXT_ESCAPE
) {
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
))
3154 else if (FILTER_TEXT_IS(FILTER_TEXT_BG_BLACK
))
3156 else if (FILTER_TEXT_IS(FILTER_TEXT_CENTER
))
3158 else if (FILTER_TEXT_IS(FILTER_TEXT_LEFT
))
3160 else if (FILTER_TEXT_IS(FILTER_TEXT_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];
3170 else if ((line_width
+ font
->width
) <= xsize
) {
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
)
3181 else // Truncate line.
3185 // Compute number of characters and width.
3188 while ((len
!= line_length
) &&
3189 (next
[len
] != '\0') &&
3190 (next
[len
] != '\n') &&
3191 (next
[len
] != *FILTER_TEXT_ESCAPE
)) {
3192 width
+= font
->width
;
3196 len
= font_text((buf
.u8
+
3197 // Horizontal offset.
3200 ((line_height
- font
->height
) * buf_pitch
)),
3202 line_height
, Bpp
, buf_pitch
, next
, len
, ~0u,
3209 static const struct filter filter_text_def
= {
3210 "text", filter_text
, true, false, false
3215 static void set_swab()
3217 const struct filter
*f
= filters_find("swab");
3226 static int prompt_cmd_filter_push(class md
&, unsigned int ac
, const char** av
)
3232 for (i
= 1; (i
!= ac
); ++i
) {
3233 const struct filter
*f
= filters_find(av
[i
]);
3242 static char* prompt_cmpl_filter_push(class md
&, unsigned int ac
,
3243 const char** av
, unsigned int len
)
3245 const struct filter
*f
;
3251 if ((ac
== 1) || (len
== ~0u) || (av
[(ac
- 1)] == NULL
)) {
3256 prefix
= av
[(ac
- 1)];
3259 for (i
= 0; (i
!= elemof(filters_available
)); ++i
) {
3260 f
= &filters_available
[i
];
3261 if (strncasecmp(prefix
, f
->name
, len
))
3267 if (i
== elemof(filters_available
)) {
3268 if (prompt
.skip
!= 0) {
3275 return strdup(f
->name
);
3278 static int prompt_cmd_filter_pop(class md
&, unsigned int ac
, const char**)
3286 static int prompt_cmd_filter_none(class md
&, unsigned int ac
, const char**)
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.
3310 prompt_cmd_calibrate(class md
&, unsigned int n_args
, const char** args
)
3312 /* check args first */
3314 calibrating_controller
= 0;
3315 else if (n_args
== 2) {
3316 calibrating_controller
= (atoi(args
[1]) - 1);
3317 if (calibrating_controller
> 1)
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.
3334 if (filters_stack
[i
]->resize
== true)
3337 while (name
+= strspn(name
, " \t\n"), name
[0] != '\0') {
3338 const struct filter
*f
;
3339 int len
= strcspn(name
, " \t\n");
3342 snprintf(token
, sizeof(token
), "%.*s", len
, name
);
3344 if (((f
= filters_find(token
)) == NULL
) ||
3345 (filters_stack_size
== elemof(filters_stack
)))
3353 * Display splash screen.
3355 static void mdscr_splash()
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
;
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
))
3371 src
.u8
= (uint8_t *)dgen_splash_data
.pixel_data
;
3372 dst
.u8
= ((uint8_t *)mdscr
.data
+ (dst_pitch
* 8) + 16);
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
) {
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
;
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
;
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
;
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
;
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
|
3445 struct screen scrtmp
;
3446 const struct dgen_font
*font
;
3449 screen_update_thread_stop();
3451 DEBUG(("want width=%u height=%u", width
, height
));
3453 // Copy current screen data.
3454 memcpy(&scrtmp
, &screen
, sizeof(scrtmp
));
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
);
3467 scrtmp
.info_height
= info_height
;
3471 scrtmp
.want_fullscreen
= 0;
3472 scrtmp
.is_fullscreen
= 0;
3474 scrtmp
.want_opengl
= 0;
3475 scrtmp
.is_opengl
= 0;
3478 scrtmp
.want_thread
= 0;
3479 scrtmp
.is_thread
= 0;
3484 memset(scrtmp
.color
, 0, sizeof(scrtmp
.color
));
3487 // Use configuration data.
3489 scrtmp
.width
= width
;
3490 if (dgen_width
>= 1)
3491 scrtmp
.width
= dgen_width
;
3493 scrtmp
.height
= height
;
3494 if (dgen_height
>= 1)
3495 scrtmp
.height
= dgen_height
;
3496 if (dgen_depth
>= 0) {
3497 scrtmp
.bpp
= dgen_depth
;
3500 // scrtmp.x_scale, scrtmp.y_scale and scrtmp.info_height cannot be
3502 scrtmp
.want_fullscreen
= !!dgen_fullscreen
;
3505 scrtmp
.want_opengl
= !!dgen_opengl
;
3508 scrtmp
.want_thread
= !!dgen_screen_thread
;
3510 // Configure SDL_SetVideoMode().
3511 if (scrtmp
.want_fullscreen
)
3512 flags
|= SDL_FULLSCREEN
;
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
;
3524 flags
|= ((dgen_doublebuffer
? SDL_DOUBLEBUF
: 0) |
3526 if (scrtmp
.want_fullscreen
) {
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)) {
3537 } best
= { 0, (unsigned int)-1, (unsigned int)-1 };
3539 // Find the best resolution available.
3540 for (i
= 0; (modes
[i
] != NULL
); ++i
) {
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
))
3548 w
= (modes
[i
]->w
- scrtmp
.width
);
3549 h
= (modes
[i
]->h
- scrtmp
.height
);
3550 if ((w
<= best
.w
) && (h
<= best
.h
)) {
3556 if ((best
.w
== (unsigned int)-1) ||
3557 (best
.h
== (unsigned int)-1))
3558 DEBUG(("no mode looks good"));
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
));
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
,
3574 if (scrtmp
.surface
== NULL
) {
3576 // Try again without OpenGL.
3577 if (flags
& SDL_OPENGL
) {
3578 assert(scrtmp
.want_opengl
);
3579 DEBUG(("OpenGL initialization failed, retrying"
3582 flags
&= ~SDL_OPENGL
;
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);
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
);
3604 scrtmp
.info_height
= 0;
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
;
3612 scrtmp
.x_scale
= (scrtmp
.width
/ video
.width
);
3613 if (dgen_y_scale
>= 0)
3614 scrtmp
.y_scale
= dgen_y_scale
;
3616 scrtmp
.y_scale
= ((scrtmp
.height
- scrtmp
.info_height
) /
3619 if (scrtmp
.x_scale
>= scrtmp
.y_scale
)
3620 scrtmp
.x_scale
= scrtmp
.y_scale
;
3622 scrtmp
.y_scale
= scrtmp
.x_scale
;
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
3629 if ((dgen_depth
== 15) && (scrtmp
.bpp
== 16))
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"
3639 " buf.u8=%p pitch=%u surface=%p want_fullscreen=%u"
3640 " is_fullscreen=%u",
3641 scrtmp
.width
, scrtmp
.height
, scrtmp
.bpp
, scrtmp
.Bpp
,
3643 (void *)scrtmp
.buf
.u8
, scrtmp
.pitch
, (void *)scrtmp
.surface
,
3644 scrtmp
.want_fullscreen
, scrtmp
.is_fullscreen
));
3646 if (scrtmp
.want_opengl
) {
3647 if (init_texture(&scrtmp
)) {
3648 DEBUG(("OpenGL initialization failed, retrying"
3651 flags
&= ~SDL_OPENGL
;
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
));
3667 // Screen is now initialized, update data.
3670 if (!screen
.is_opengl
) {
3671 // Free OpenGL resources.
3672 DEBUG(("releasing OpenGL resources"));
3673 release_texture(screen
.texture
);
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
;
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
));
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
;
3709 if (screen
.want_thread
)
3710 screen_update_thread_start();
3713 filters_stack_update();
3715 pd_graphics_update(true);
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
)
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")));
3736 #ifdef HAVE_SDL_WM_TOGGLEFULLSCREEN
3738 DEBUG(("trying SDL_WM_ToggleFullScreen(%p)", (void *)screen
.surface
));
3739 if (SDL_WM_ToggleFullScreen(screen
.surface
))
3741 DEBUG(("falling back to screen_init()"));
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.
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
)
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
);
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);
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());
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;
3818 // Required for text input.
3819 SDL_EnableUNICODE(1);
3820 // Set the titlebar.
3821 SDL_WM_SetCaption("DGen/SDL " VER
, "DGen/SDL " VER
);
3824 // Initialize screen.
3825 if (screen_init(0, 0))
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"));
3833 // We don't need setuid privileges anymore
3834 if (getuid() != geteuid())
3836 DEBUG(("setuid privileges dropped"));
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
))
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
);
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
)));
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
);
3867 while (SDL_PollEvent(&event
)) {
3868 switch (event
.type
) {
3869 case SDL_VIDEORESIZE
:
3870 if (screen_init(event
.resize
.w
, event
.resize
.h
))
3877 fprintf(stderr
, "sdl: can't initialize graphics.\n");
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
);
3905 // Reinitialize screen.
3906 if (screen_init(screen
.window_width
, screen
.window_height
))
3908 DEBUG(("screen reinitialized"));
3911 fprintf(stderr
, "sdl: can't reinitialize graphics.\n");
3918 void pd_graphics_palette_update()
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)];
3928 if (!screen
.is_opengl
)
3930 SDL_SetColors(screen
.surface
, screen
.color
, 0, 64);
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
;
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);
3956 if (tmp
>= 1000000) {
3960 if (frames_old
> frames
)
3961 fps
= (frames_old
- frames
);
3963 fps
= (frames
- frames_old
);
3964 frames_old
= frames
;
3965 if (!info
.displayed
) {
3968 snprintf(buf
, sizeof(buf
), "%lu FPS", fps
);
3969 pd_message_write(buf
, strlen(buf
), ~0u);
3973 if (update
== false)
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)))
3982 f
->func(fd
, (fd
+ 1));
3987 // Generate screen output with the last filter.
3988 f
->func(fd
, (fd
+ 1));
3991 // Update the screen.
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
)
4004 // Slurp off the play buffer
4005 wrote
= cbuf_read(stream
, &sound
.cbuf
, len
);
4006 if (wrote
== (size_t)len
)
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
;
4026 // Set the desired format
4028 #ifdef WORDS_BIGENDIAN
4029 wanted
.format
= AUDIO_S16MSB
;
4031 wanted
.format
= AUDIO_S16LSB
;
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");
4043 // Open audio, and get the real spec
4044 if (SDL_OpenAudio(&wanted
, &spec
) < 0) {
4046 "sdl: couldn't open audio: %s\n",
4052 if (spec
.channels
!= 2) {
4053 fprintf(stderr
, "sdl: couldn't get stereo audio format.\n");
4056 if (spec
.format
!= wanted
.format
) {
4057 fprintf(stderr
, "sdl: unable to get 16-bit audio.\n");
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)));
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");
4084 // Start sound output.
4091 // Oops! Something bad happened, cleanup.
4093 free((void *)sndi
.lr
);
4096 free((void *)sound
.cbuf
.data
.i16
);
4097 sound
.cbuf
.data
.i16
= NULL
;
4098 memset(&sound
, 0, sizeof(sound
));
4103 * Deinitialize sound subsystem.
4105 void pd_sound_deinit()
4107 if (sound
.cbuf
.data
.i16
!= NULL
) {
4110 free((void *)sound
.cbuf
.data
.i16
);
4112 memset(&sound
, 0, sizeof(sound
));
4113 free((void*)sndi
.lr
);
4118 * Return samples read/write indices in the buffer.
4120 unsigned int pd_sound_rp()
4124 if (!sound
.cbuf
.size
)
4132 unsigned int pd_sound_wp()
4136 if (!sound
.cbuf
.size
)
4139 ret
= ((sound
.cbuf
.i
+ sound
.cbuf
.s
) % sound
.cbuf
.size
);
4145 * Write contents of sndi to sound.cbuf.
4147 void pd_sound_write()
4149 if (!sound
.cbuf
.size
)
4152 cbuf_write(&sound
.cbuf
, (uint8_t *)sndi
.lr
, (sndi
.len
* 4));
4157 * Tells whether DGen stopped intentionally so emulation can resume without
4178 * Keyboard input results.
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
,
4197 #define HISTORY_LEN 32
4198 static char history
[HISTORY_LEN
][64];
4199 static int history_pos
= -1;
4200 static int history_len
= 0;
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
;
4212 return KB_INPUT_CONSUMED
;
4214 else if (ksym
== SDLK_DELETE
) {
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)],
4223 return KB_INPUT_CONSUMED
;
4225 else if (ksym
== SDLK_BACKSPACE
) {
4228 if (input
->pos
== 0)
4229 return KB_INPUT_CONSUMED
;
4231 tail
= ((input
->size
- input
->pos
) + 1);
4232 memmove(&input
->buf
[input
->pos
],
4233 &input
->buf
[(input
->pos
+ 1)],
4235 return KB_INPUT_CONSUMED
;
4237 else if (ksym
== SDLK_LEFT
) {
4238 if (input
->pos
!= 0)
4240 return KB_INPUT_CONSUMED
;
4242 else if (ksym
== SDLK_RIGHT
) {
4243 if (input
->buf
[input
->pos
] != '\0')
4245 return KB_INPUT_CONSUMED
;
4247 else if ((ksym
== SDLK_RETURN
) || (ksym
== SDLK_KP_ENTER
)) {
4249 if (input
->pos
== 0)
4250 return KB_INPUT_ABORTED
;
4251 if (history_len
< 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
) {
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))
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)
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
4289 static void pd_message_cursor(unsigned int mark
, const char *msg
, ...)
4297 len
= (size_t)vsnprintf(buf
, sizeof(buf
), msg
, vl
);
4299 buf
[(sizeof(buf
) - 1)] = '\0';
4300 disp_len
= font_text_max_len(screen
.width
, screen
.info_height
,
4303 if (len
<= disp_len
)
4304 pd_message_display(buf
, len
, ~0u, true);
4306 pd_message_display(&(buf
[(len
- disp_len
)]), disp_len
,
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
)
4328 bool init_video
= false;
4329 bool init_sound
= false;
4330 bool init_joystick
= false;
4332 if (rc
->variable
== &dgen_craptv
) {
4334 filters_pluck_ctv();
4335 filters_insert(filters_find(ctv_names
[dgen_craptv
% NUM_CTV
]));
4340 else if (rc
->variable
== &dgen_scaling
) {
4341 if (set_scaling(scaling_names
[dgen_scaling
]) == 0)
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
) {
4350 megad
.z80_core
= md::Z80_CORE_MZ80
;
4355 megad
.z80_core
= md::Z80_CORE_CZ80
;
4360 megad
.z80_core
= md::Z80_CORE_DRZ80
;
4364 megad
.z80_core
= md::Z80_CORE_NONE
;
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
) {
4375 megad
.cpu_emu
= md::CPU_EMU_STAR
;
4380 megad
.cpu_emu
= md::CPU_EMU_MUSA
;
4385 megad
.cpu_emu
= md::CPU_EMU_CYCLONE
;
4389 megad
.cpu_emu
= md::CPU_EMU_NONE
;
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
))
4400 else if (rc
->variable
== &dgen_fullscreen
) {
4401 if (screen
.want_fullscreen
!= (!!dgen_fullscreen
)) {
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
))
4414 else if (rc
->variable
== &dgen_swab
) {
4421 else if ((rc
->variable
== &dgen_scale
) ||
4422 (rc
->variable
== &dgen_aspect
)) {
4423 dgen_x_scale
= dgen_scale
;
4424 dgen_y_scale
= dgen_scale
;
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
)) {
4438 else if (rc
->variable
== &dgen_joystick
)
4439 init_joystick
= true;
4440 else if (rc
->variable
== &dgen_hz
) {
4444 else if (dgen_hz
> 1000)
4446 if (((unsigned int)dgen_hz
!= video
.hz
) ||
4447 ((unsigned int)dgen_hz
!= megad
.vhz
)) {
4453 else if (rc
->variable
== &dgen_pal
) {
4456 ((video
.is_pal
== false) ||
4458 (video
.height
!= PAL_VBLANK
))) {
4461 video
.is_pal
= true;
4462 video
.height
= PAL_VBLANK
;
4465 else if ((!dgen_pal
) &&
4466 ((video
.is_pal
== true) ||
4468 (video
.height
!= NTSC_VBLANK
))) {
4471 video
.is_pal
= false;
4472 video
.height
= NTSC_VBLANK
;
4476 else if (rc
->variable
== &dgen_region
) {
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
)) {
4495 video
.height
= vblank
;
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
);
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
)) {
4523 else if (dgen_sound
== 0)
4526 uint8_t ym2612_buf
[512];
4527 uint8_t sn76496_buf
[16];
4528 unsigned int samples
;
4529 long rate
= dgen_soundrate
;
4532 samples
= (dgen_soundsegs
* (rate
/ video
.hz
));
4533 if (!pd_sound_init(rate
, samples
))
4535 YM2612_dump(0, ym2612_buf
);
4536 SN76496_dump(0, sn76496_buf
);
4538 SN76496_restore(0, sn76496_buf
);
4539 YM2612_restore(0, ym2612_buf
);
4542 if (init_joystick
) {
4543 #ifdef WITH_JOYSTICK
4544 megad
.deinit_joysticks();
4546 megad
.init_joysticks();
4552 pd_message("Failed to rehash value.");
4553 return (PROMPT_RET_EXIT
| PROMPT_RET_MSG
);
4555 return PROMPT_RET_CONT
;
4557 pd_message("Failed to reinitialize video.");
4558 return (PROMPT_RET_EXIT
| PROMPT_RET_MSG
);
4560 fprintf(stderr
, "sdl: fatal error while trying to change screen"
4562 return (PROMPT_RET_ERROR
| PROMPT_RET_MSG
);
4565 static void prompt_show_rc_field(const struct rc_field
*rc
)
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
);
4579 pd_message("%s is bound to \"%s\"", rc
->fieldname
, 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
);
4591 pd_message("%s is bound to \"%s\"", rc
->fieldname
, 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
);
4600 pd_message("%s is bound to \"%s\"", rc
->fieldname
, mo
);
4603 else if (rc
->parser
== rc_ctv
) {
4606 pd_message("%s is undefined", rc
->fieldname
);
4608 pd_message("%s is \"%s\"", rc
->fieldname
,
4611 else if (rc
->parser
== rc_scaling
) {
4613 if (i
>= NUM_SCALING
)
4614 pd_message("%s is undefined", rc
->fieldname
);
4616 pd_message("%s is \"%s\"", rc
->fieldname
,
4619 else if (rc
->parser
== rc_emu_z80
) {
4620 for (i
= 0; (emu_z80_names
[i
] != NULL
); ++i
)
4621 if (i
== (size_t)val
)
4623 if (emu_z80_names
[i
] == NULL
)
4624 pd_message("%s is undefined", rc
->fieldname
);
4626 pd_message("%s is \"%s\"", rc
->fieldname
,
4629 else if (rc
->parser
== rc_emu_m68k
) {
4630 for (i
= 0; (emu_m68k_names
[i
] != NULL
); ++i
)
4631 if (i
== (size_t)val
)
4633 if (emu_m68k_names
[i
] == NULL
)
4634 pd_message("%s is undefined", rc
->fieldname
);
4636 pd_message("%s is \"%s\"", rc
->fieldname
,
4639 else if (rc
->parser
== rc_region
) {
4643 s
= "America (NTSC)";
4644 else if (val
== 'E')
4646 else if (val
== 'J')
4648 else if (val
== 'X')
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
;
4660 if (rs
->val
== NULL
)
4661 pd_message("%s has no value", rc
->fieldname
);
4662 else if ((s
= backslashify((const uint8_t *)rs
->val
,
4665 pd_message("%s is \"%s\"", rc
->fieldname
, s
);
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
;
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
);
4682 pd_message("%s is bound to \"%s\"", f
, s
);
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
;
4696 bool binding_tried
= false;
4698 if (prompt_parse(p
, &pp
) == NULL
)
4699 return PROMPT_RET_ERROR
;
4701 ret
= PROMPT_RET_EXIT
;
4705 // Look for a command with that name.
4706 for (i
= 0; (prompt_command
[i
].name
!= NULL
); ++i
) {
4709 if (strcasecmp(prompt_command
[i
].name
, (char *)pp
.argv
[0]))
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
;
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
;
4728 // Look for a variable with that name.
4729 for (i
= 0; (rc_fields
[i
].fieldname
!= NULL
); ++i
) {
4732 if (strcasecmp(rc_fields
[i
].fieldname
, (char *)pp
.argv
[0]))
4734 // Display current value?
4735 if (pp
.argv
[1] == NULL
) {
4736 prompt_show_rc_field(&rc_fields
[i
]);
4737 ret
|= PROMPT_RET_MSG
;
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
;
4748 if ((rc_fields
[i
].parser
== rc_string
) ||
4749 (rc_fields
[i
].parser
== rc_rom_path
)) {
4752 rs
= (struct rc_str
*)rc_fields
[i
].variable
;
4753 if (rc_str_list
== NULL
) {
4754 atexit(rc_str_cleanup
);
4757 else if (rs
->alloc
== NULL
) {
4758 rs
->next
= rc_str_list
;
4763 rs
->alloc
= (char *)potential
;
4764 rs
->val
= rs
->alloc
;
4767 *(rc_fields
[i
].variable
) = potential
;
4768 ret
|= prompt_rehash_rc_field(&rc_fields
[i
], md
);
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;
4777 pd_message("%s: unknown command", (char *)pp
.argv
[0]);
4778 ret
|= PROMPT_RET_MSG
;
4781 prompt_parse_clean(&pp
);
4783 ret
|= PROMPT_RET_ENTER
;
4787 static void handle_prompt_complete_clear()
4789 complete_path_free(prompt
.complete
);
4790 prompt
.complete
= NULL
;
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
4806 if (prompt_parse(p
, &pp
) == NULL
)
4807 return PROMPT_RET_ERROR
;
4810 if (pp
.index
== 0) {
4811 const char *cs
= NULL
;
4812 const char *cm
= NULL
;
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];
4821 if ((arg
== NULL
) || (alen
== ~0u)) {
4828 for (i
= 0; (prompt_command
[i
].name
!= NULL
); ++i
) {
4829 if (strncasecmp(prompt_command
[i
].name
, arg
, alen
))
4832 tmp
= strlen(prompt_command
[i
].name
);
4834 tmp
= strcommon(prompt_command
[i
].name
, cm
);
4835 cm
= prompt_command
[i
].name
;
4843 cs
= prompt_command
[i
].name
;
4845 goto complete_cmd_found
;
4848 for (i
= 0; (rc_fields
[i
].fieldname
!= NULL
); ++i
) {
4849 if (strncasecmp(rc_fields
[i
].fieldname
, arg
, alen
))
4852 tmp
= strlen(rc_fields
[i
].fieldname
);
4854 tmp
= strcommon(rc_fields
[i
].fieldname
, cm
);
4855 cm
= rc_fields
[i
].fieldname
;
4863 cs
= rc_fields
[i
].fieldname
;
4868 // Nothing matched, try again if possible.
4871 goto complete_cmd_var
;
4877 s
= backslashify((const uint8_t *)cs
, strlen(cs
), 0, &common
);
4880 if (common
!= ~0u) {
4881 prompt
.common
= common
;
4882 prompt_common
= common
;
4886 // Complete function arguments.
4887 for (i
= 0; (prompt_command
[i
].name
!= NULL
); ++i
) {
4890 if (strcasecmp(prompt_command
[i
].name
,
4891 (const char *)pp
.argv
[0]))
4893 if (prompt_command
[i
].cmpl
== NULL
)
4895 t
= prompt_command
[i
].cmpl(md
, pp
.argc
, (const char **)pp
.argv
,
4899 prompt_common
= prompt
.common
;
4900 s
= backslashify((const uint8_t *)t
, strlen(t
), 0,
4907 // Variable value completion.
4908 arg
= (const char *)pp
.argv
[pp
.index
];
4910 if ((arg
== NULL
) || (alen
== ~0u)) {
4914 for (i
= 0; (rc_fields
[i
].fieldname
!= NULL
); ++i
) {
4915 struct rc_field
*rc
= &rc_fields
[i
];
4918 if (strcasecmp(rc
->fieldname
, (const char *)pp
.argv
[0]))
4921 if (rc
->parser
== rc_boolean
)
4922 s
= strdup((prompt
.skip
& 1) ? "true" : "false");
4924 else if (rc
->parser
== rc_rom_path
) {
4925 if (prompt
.complete
== NULL
) {
4927 complete_path(arg
, alen
, NULL
);
4929 rehash_prompt_complete_common();
4931 if (prompt
.complete
!= NULL
) {
4932 char **ret
= prompt
.complete
;
4936 for (i
= 0; (ret
[i
] != NULL
); ++i
) {
4941 if (ret
[i
] == NULL
) {
4942 if (prompt
.skip
!= 0) {
4944 goto rc_rom_path_retry
;
4948 prompt_common
= prompt
.common
;
4950 ((const uint8_t *)ret
[i
],
4957 else if ((rc
->parser
== rc_number
) ||
4958 (rc
->parser
== rc_soundrate
)) {
4962 if (snprintf(buf
, sizeof(buf
), "%d",
4963 (int)prompt
.skip
) >= (int)sizeof(buf
)) {
4965 goto rc_number_retry
;
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
)) {
4976 for (i
= 0; (names
[i
] != NULL
); ++i
) {
4981 if (names
[i
] == NULL
) {
4982 if (prompt
.skip
!= 0) {
4984 goto rc_names_retry
;
4988 s
= strdup(names
[i
]);
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
) {
5007 handle_prompt_complete_clear();
5012 prompt_parse_clean(&pp
);
5016 static int handle_prompt(uint32_t ksym
, uint16_t ksym_uni
, md
& megad
)
5018 struct prompt::prompt_history
*ph
;
5022 int ret
= PROMPT_RET_CONT
;
5023 struct prompt
*p
= &prompt
.status
;
5024 struct prompt_parse pp
;
5028 switch (ksym
& ~KEYSYM_MOD_MASK
) {
5030 handle_prompt_complete_clear();
5034 handle_prompt_complete_clear();
5038 handle_prompt_complete_clear();
5042 handle_prompt_complete_clear();
5046 handle_prompt_complete_clear();
5050 handle_prompt_complete_clear();
5053 case SDLK_BACKSPACE
:
5054 handle_prompt_complete_clear();
5055 prompt_backspace(p
);
5058 handle_prompt_complete_clear();
5063 if ((ksym
& KEYSYM_MOD_CTRL
) == 0)
5065 handle_prompt_complete_clear();
5070 if ((ksym
& KEYSYM_MOD_CTRL
) == 0)
5072 handle_prompt_complete_clear();
5077 if ((ksym
& KEYSYM_MOD_CTRL
) == 0)
5079 handle_prompt_complete_clear();
5084 if ((ksym
& KEYSYM_MOD_CTRL
) == 0)
5086 handle_prompt_complete_clear();
5087 prompt_replace(p
, p
->cursor
, ~0u, NULL
, 0);
5091 if ((ksym
& KEYSYM_MOD_CTRL
) == 0)
5093 if (prompt_parse(p
, &pp
) == NULL
)
5095 if (pp
.argv
[pp
.index
] == NULL
) {
5096 if (pp
.index
== 0) {
5097 prompt_parse_clean(&pp
);
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
),
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
);
5115 handle_prompt_complete_clear();
5116 ret
|= handle_prompt_enter(megad
);
5119 handle_prompt_complete_clear();
5120 ret
|= PROMPT_RET_EXIT
;
5123 if (ksym
& KEYSYM_MOD_SHIFT
)
5124 ret
|= handle_prompt_complete(megad
, true);
5126 ret
|= handle_prompt_complete(megad
, false);
5132 handle_prompt_complete_clear();
5133 sz
= utf32u8(c
, ksym_uni
);
5135 ((s
= backslashify(c
, sz
, BACKSLASHIFY_NOQUOTES
,
5139 for (i
= 0; (i
!= strlen(s
)); ++i
)
5140 prompt_put(p
, s
[i
]);
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
);
5154 // Controls enum. You must add new entries at the end. Do not change the order.
5185 CTL_PICO_PEN_BUTTON
,
5188 CTL_DGEN_CRAPTV_TOGGLE
,
5189 CTL_DGEN_SCALING_TOGGLE
,
5205 CTL_DGEN_Z80_TOGGLE
,
5206 CTL_DGEN_CPU_TOGGLE
,
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
,
5219 // Controls definitions.
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;
5232 static int ctl_pad1(struct ctl
& ctl
, md
& megad
)
5236 megad
.pad
[0] &= ~MD_UP_MASK
;
5239 megad
.pad
[0] &= ~MD_DOWN_MASK
;
5242 megad
.pad
[0] &= ~MD_LEFT_MASK
;
5244 case CTL_PAD1_RIGHT
:
5245 megad
.pad
[0] &= ~MD_RIGHT_MASK
;
5248 megad
.pad
[0] &= ~MD_A_MASK
;
5251 megad
.pad
[0] &= ~MD_B_MASK
;
5254 megad
.pad
[0] &= ~MD_C_MASK
;
5257 megad
.pad
[0] &= ~MD_X_MASK
;
5260 megad
.pad
[0] &= ~MD_Y_MASK
;
5263 megad
.pad
[0] &= ~MD_Z_MASK
;
5266 megad
.pad
[0] &= ~MD_MODE_MASK
;
5268 case CTL_PAD1_START
:
5269 megad
.pad
[0] &= ~MD_START_MASK
;
5277 static int ctl_pad1_release(struct ctl
& ctl
, md
& megad
)
5281 megad
.pad
[0] |= MD_UP_MASK
;
5284 megad
.pad
[0] |= MD_DOWN_MASK
;
5287 megad
.pad
[0] |= MD_LEFT_MASK
;
5289 case CTL_PAD1_RIGHT
:
5290 megad
.pad
[0] |= MD_RIGHT_MASK
;
5293 megad
.pad
[0] |= MD_A_MASK
;
5296 megad
.pad
[0] |= MD_B_MASK
;
5299 megad
.pad
[0] |= MD_C_MASK
;
5302 megad
.pad
[0] |= MD_X_MASK
;
5305 megad
.pad
[0] |= MD_Y_MASK
;
5308 megad
.pad
[0] |= MD_Z_MASK
;
5311 megad
.pad
[0] |= MD_MODE_MASK
;
5313 case CTL_PAD1_START
:
5314 megad
.pad
[0] |= MD_START_MASK
;
5322 static int ctl_pad2(struct ctl
& ctl
, md
& megad
)
5326 megad
.pad
[1] &= ~MD_UP_MASK
;
5329 megad
.pad
[1] &= ~MD_DOWN_MASK
;
5332 megad
.pad
[1] &= ~MD_LEFT_MASK
;
5334 case CTL_PAD2_RIGHT
:
5335 megad
.pad
[1] &= ~MD_RIGHT_MASK
;
5338 megad
.pad
[1] &= ~MD_A_MASK
;
5341 megad
.pad
[1] &= ~MD_B_MASK
;
5344 megad
.pad
[1] &= ~MD_C_MASK
;
5347 megad
.pad
[1] &= ~MD_X_MASK
;
5350 megad
.pad
[1] &= ~MD_Y_MASK
;
5353 megad
.pad
[1] &= ~MD_Z_MASK
;
5356 megad
.pad
[1] &= ~MD_MODE_MASK
;
5358 case CTL_PAD2_START
:
5359 megad
.pad
[1] &= ~MD_START_MASK
;
5367 static int ctl_pad2_release(struct ctl
& ctl
, md
& megad
)
5371 megad
.pad
[1] |= MD_UP_MASK
;
5374 megad
.pad
[1] |= MD_DOWN_MASK
;
5377 megad
.pad
[1] |= MD_LEFT_MASK
;
5379 case CTL_PAD2_RIGHT
:
5380 megad
.pad
[1] |= MD_RIGHT_MASK
;
5383 megad
.pad
[1] |= MD_A_MASK
;
5386 megad
.pad
[1] |= MD_B_MASK
;
5389 megad
.pad
[1] |= MD_C_MASK
;
5392 megad
.pad
[1] |= MD_X_MASK
;
5395 megad
.pad
[1] |= MD_Y_MASK
;
5398 megad
.pad
[1] |= MD_Z_MASK
;
5401 megad
.pad
[1] |= MD_MODE_MASK
;
5403 case CTL_PAD2_START
:
5404 megad
.pad
[1] |= MD_START_MASK
;
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 {
5422 unsigned int coords
:1;
5424 unsigned int lim
[2];
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
} }
5433 if (ctl
.type
== CTL_PICO_PEN_BUTTON
) {
5434 megad
.pad
[0] &= ~MD_PICO_PENBTN_MASK
;
5437 // Use coordinates if available.
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
));
5449 for (i
= 0; (i
!= elemof(motion
)); ++i
) {
5450 unsigned int coords
;
5452 if (motion
[i
].type
!= ctl
.type
)
5454 coords
= motion
[i
].coords
;
5456 megad
.pico_pen_coords
[coords
] += pico_pen_stride
;
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
];
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
;
5477 static int ctl_dgen_quit(struct ctl
&, md
&)
5482 static int ctl_dgen_craptv_toggle(struct ctl
&, md
&)
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
]);
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
]);
5500 pd_message("Using scaling algorithm \"%s\".",
5501 scaling_names
[dgen_scaling
]);
5505 static int ctl_dgen_reset(struct ctl
&, md
& megad
)
5508 pd_message("Genesis reset.");
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
);
5519 static int ctl_dgen_slot_next(struct ctl
&, md
&)
5525 pd_message("Selected next save slot (%d).", slot
);
5529 static int ctl_dgen_slot_prev(struct ctl
&, md
&)
5535 pd_message("Selected previous save slot (%d).", slot
);
5539 static int ctl_dgen_save(struct ctl
&, md
& megad
)
5545 static int ctl_dgen_load(struct ctl
&, md
& megad
)
5552 static int ctl_dgen_z80_toggle(struct ctl
&, md
& megad
)
5557 switch (megad
.z80_core
) {
5559 case md::Z80_CORE_CZ80
:
5560 msg
= "CZ80 core activated.";
5564 case md::Z80_CORE_MZ80
:
5565 msg
= "MZ80 core activated.";
5569 case md::Z80_CORE_DRZ80
:
5570 msg
= "DrZ80 core activated.";
5574 msg
= "Z80 core disabled.";
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
)
5588 switch (megad
.cpu_emu
) {
5590 case md::CPU_EMU_STAR
:
5591 msg
= "StarScream CPU core activated.";
5595 case md::CPU_EMU_MUSA
:
5596 msg
= "Musashi CPU core activated.";
5600 case md::CPU_EMU_CYCLONE
:
5601 msg
= "Cyclone CPU core activated.";
5605 msg
= "CPU core disabled.";
5612 static int ctl_dgen_stop(struct ctl
&, md
& megad
)
5614 pd_message(stopped_str
);
5615 if (stop_events(megad
, STOPPED
) != 0)
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)
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)
5636 static int ctl_dgen_volume(struct ctl
& ctl
, md
&)
5638 if (ctl
.type
== CTL_DGEN_VOLUME_INC
)
5642 if (dgen_volume
< 0)
5644 else if (dgen_volume
> 100)
5646 pd_message("Volume %d%%.", (int)dgen_volume
);
5650 static int ctl_dgen_fullscreen_toggle(struct ctl
&, md
&)
5652 switch (set_fullscreen(!screen
.is_fullscreen
)) {
5655 "sdl: fatal error while trying to change screen"
5659 pd_message("Failed to toggle fullscreen mode.");
5662 pd_message("Fullscreen mode toggled.");
5667 static int ctl_dgen_fix_checksum(struct ctl
&, md
& megad
)
5669 pd_message("Checksum fixed.");
5670 megad
.fix_rom_checksum();
5674 static int ctl_dgen_screenshot(struct ctl
&, md
& megad
)
5676 do_screenshot(megad
);
5680 static int ctl_dgen_debug_enter(struct ctl
&, md
& megad
)
5682 #ifdef WITH_DEBUGGER
5684 if (megad
.debug_trap
== false)
5685 megad
.debug_enter();
5687 megad
.debug_leave();
5690 pd_message("Debugger support not built in.");
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
},
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
},
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
}
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);
5823 // Stop emulation, enter calibration mode.
5826 filter_text_str
[0] = '\0';
5827 filter_text_msg(FILTER_TEXT_BG_BLACK
5830 "CONTROLLER %u CALIBRATION\n"
5834 "Press each button twice,\n"
5835 "or two different buttons to skip them.\n"
5837 (calibrating_controller
+ 1));
5838 filters_pluck(&filter_text_def
);
5839 filters_insert(&filter_text_def
);
5842 while (step
!= elemof(calibration_steps
))
5843 if ((calibration_steps
[step
].once
== true) &&
5844 (calibration_steps
[step
].twice
== true))
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.
5858 calibrating
= false;
5859 filters_pluck(&filter_text_def
);
5862 if (calibration_steps
[step
].once
== false) {
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
);
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
: ""));
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");
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))
5896 if (step
== elemof(calibration_steps
)) {
5897 code
= calibration_steps
[(elemof(calibration_steps
) - 1)].code
;
5899 filter_text_msg("\n"
5904 for (i
= 0; (i
!= elemof(calibration_steps
)); ++i
) {
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"
5920 else if (calibration_steps
[step
].name
!= NULL
)
5921 filter_text_msg("%s: ", calibration_steps
[step
].name
);
5923 filter_text_msg("\n"
5924 "Press any button twice to apply settings:\n"
5928 static struct rc_binding_item combos
[64];
5930 static void manage_combos(md
& md
, bool pressed
, enum rc_binding_type type
,
5936 for (i
= 0; (i
!= elemof(combos
)); ++i
) {
5937 if (!combos
[i
].assigned
) {
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
;
5946 if ((combos
[i
].type
!= type
) || (combos
[i
].code
!= code
))
5947 continue; // Does not match.
5949 return; // Already pressed.
5951 memmove(&combos
[i
], &combos
[i
+ 1],
5952 ((elemof(combos
) - (i
+ 1)) * sizeof(combos
[i
])));
5957 static bool check_combos(md
& md
, struct rc_binding_item item
[],
5961 unsigned int found
= 0;
5964 for (i
= 0; (i
!= num
); ++i
) {
5967 if (!item
[i
].assigned
) {
5971 for (j
= 0; (j
!= elemof(combos
)); ++j
) {
5972 if (!combos
[j
].assigned
)
5974 if ((combos
[j
].type
!= item
[i
].type
) ||
5975 (combos
[j
].code
!= item
[i
].code
))
5983 return (found
== num
);
5986 static int manage_bindings(md
& md
, bool pressed
, enum rc_binding_type type
,
5989 struct rc_binding
*rcb
= rc_binding_head
.next
;
5993 if ((dgen_buttons
) && (pressed
)) {
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
);
6005 pd_message("Pressed \"%s\".", dump
);
6009 while (rcb
!= &rc_binding_head
) {
6011 (!check_combos(md
, rcb
->item
, elemof(rcb
->item
)))) {
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
)) {
6031 while (ctl
->rc
!= NULL
) {
6032 if (&(*ctl
->rc
)[type
] !=
6039 assert(ctl
->press
!= NULL
);
6040 if (!ctl
->press(*ctl
, md
))
6043 else if (ctl
->release
!= NULL
) {
6044 if (!ctl
->release(*ctl
, md
))
6052 // Otherwise, pass it to the prompt.
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
)
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
;
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
);
6083 pd_message("Invalid code.");
6085 pd_message("Reverted.");
6087 pd_message("Applied.");
6089 case KB_INPUT_ABORTED
:
6090 pd_message("Aborted.");
6093 case KB_INPUT_CONSUMED
:
6094 pd_message_cursor((len
+ input
.pos
), "%s%.*s", game_genie_str
,
6095 (int)input
.pos
, buf
);
6097 case KB_INPUT_IGNORED
:
6104 input
.size
= sizeof(buf
);
6105 memset(buf
, 0, sizeof(buf
));
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
)
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
)
6133 (control
[CTL_PICO_PEN_UP
], megad
);
6134 if (control
[CTL_PICO_PEN_DOWN
].pressed
)
6136 (control
[CTL_PICO_PEN_DOWN
], megad
);
6137 if (control
[CTL_PICO_PEN_LEFT
].pressed
)
6139 (control
[CTL_PICO_PEN_LEFT
], megad
);
6140 if (control
[CTL_PICO_PEN_RIGHT
].pressed
)
6142 (control
[CTL_PICO_PEN_RIGHT
], megad
);
6143 pico_pen_last_update
= pico_pen_now
;
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
)) {
6161 pd_message("Mouse trapped. Stop emulation to release.");
6162 SDL_WM_GrabInput(SDL_GRAB_ON
);
6164 else if ((!grab
) && (mode
== SDL_GRAB_ON
)) {
6166 SDL_WM_GrabInput(SDL_GRAB_OFF
);
6170 static int stop_events(md
& megad
, enum events status
)
6177 // Release controls.
6178 for (ctl
= control
; (ctl
->rc
!= NULL
); ++ctl
) {
6179 if (ctl
->pressed
== false)
6181 ctl
->pressed
= 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)
6193 pd_graphics_update(true);
6199 static void restart_events(md
& megad
)
6204 handle_prompt_complete_clear();
6205 SDL_EnableKeyRepeat(0, 0);
6210 unsigned long when
[0x100];
6211 uint8_t enabled
[0x100 / 8];
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
));
6229 if (!MOUSE_MOTION_RELEASE_IS_ENABLED(which
))
6231 MOUSE_MOTION_RELEASE_DISABLE(which
);
6232 assert(mouse_motion_release
.count
!= 0);
6233 --mouse_motion_release
.count
;
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
)
6251 if (mouse_motion_release
.count
== 0)
6254 for (i
= 0; (i
!= mouse_motion_release
.count
); ++i
) {
6257 if (!MOUSE_MOTION_RELEASE_IS_ENABLED(i
))
6259 diff
= (mouse_motion_release
.when
[i
] - now
);
6260 if (diff
< (unsigned long)(dgen_mouse_delay
* 1000))
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
;
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
6280 int pd_handle_events(md
&megad
)
6282 static uint16_t kpress
[0x100];
6283 #ifdef WITH_DEBUGGER
6284 static bool debug_trap
;
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
;
6308 unsigned int i
, pi
, ri
;
6315 #ifdef WITH_DEBUGGER
6316 if ((megad
.debug_trap
) && (megad
.debug_enter() < 0))
6318 if (debug_trap
!= megad
.debug_trap
) {
6319 debug_trap
= megad
.debug_trap
;
6322 if (sound
.cbuf
.size
)
6323 SDL_PauseAudio(debug_trap
== true);
6327 ((hide_mouse_when
- pd_usecs()) >= MOUSE_SHOW_USECS
)) {
6328 if (!mouse_is_grabbed())
6333 if (mouse_motion_released(&event
))
6335 if (!SDL_PollEvent(&event
)) {
6337 manage_pico_pen(megad
);
6341 switch (event
.type
) {
6342 #ifdef WITH_JOYSTICK
6343 case SDL_JOYAXISMOTION
:
6344 if (event
.jaxis
.value
<= -16384)
6346 else if (event
.jaxis
.value
>= 16384)
6350 plist
[0] = JS_AXIS(event
.jaxis
.which
,
6353 rlist
[0] = JS_AXIS(event
.jaxis
.which
,
6356 rlist
[1] = JS_AXIS(event
.jaxis
.which
,
6359 // "between" causes problems during calibration, ignore it.
6360 if (axis_value
[i
][0] == JS_AXIS_BETWEEN
)
6366 case SDL_JOYHATMOTION
:
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
,
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
,
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
)
6390 for (i
= 0; ((calibrating
) && (i
!= pi
)); ++i
)
6391 manage_calibration(RCBJ
, plist
[i
]);
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
]))
6400 ctl
->pressed
= false;
6402 if ((ctl
->release
!= NULL
) &&
6403 (ctl
->release(*ctl
, megad
) == 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;
6411 if (ctl
->press(*ctl
, megad
) == 0)
6416 for (i
= 0; (i
!= ri
); ++i
)
6417 if (!manage_bindings(megad
, false, RCBJ
, rlist
[i
]))
6419 for (i
= 0; (i
!= pi
); ++i
)
6420 if (!manage_bindings(megad
, true, RCBJ
, plist
[i
]))
6423 case SDL_JOYBUTTONDOWN
:
6424 assert(event
.jbutton
.state
== SDL_PRESSED
);
6427 case SDL_JOYBUTTONUP
:
6428 assert(event
.jbutton
.state
== SDL_RELEASED
);
6431 joypad
= JS_BUTTON(event
.jbutton
.which
, event
.jbutton
.button
);
6432 manage_combos(megad
, pressed
, RCBJ
, joypad
);
6433 if (events
!= STARTED
)
6437 manage_calibration(RCBJ
, joypad
);
6440 for (struct ctl
* ctl
= control
; (ctl
->rc
!= NULL
); ++ctl
) {
6441 if ((*ctl
->rc
)[RCBJ
] != joypad
)
6443 ctl
->pressed
= pressed
;
6445 if (pressed
== false) {
6446 if ((ctl
->release
!= NULL
) &&
6447 (ctl
->release(*ctl
, megad
) == 0))
6451 assert(ctl
->press
!= NULL
);
6452 if (ctl
->press(*ctl
, megad
) == 0)
6456 if (manage_bindings(megad
, pressed
, RCBJ
, joypad
) == 0)
6459 #endif // WITH_JOYSTICK
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
)))
6466 kpress
[(ksym
& 0xff)] = 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
);
6483 manage_calibration(RCBK
, ksym
);
6493 case STOPPED_PROMPT
:
6494 ret
= handle_prompt(ksym
, ksym_uni
, megad
);
6495 if (ret
& PROMPT_RET_ERROR
) {
6496 restart_events(megad
);
6499 if (ret
& PROMPT_RET_EXIT
) {
6500 if (events
== STOPPED_PROMPT
) {
6501 // Return to stopped mode.
6502 pd_message(stopped_str
);
6506 if ((ret
& PROMPT_RET_MSG
) == 0)
6507 pd_message("RUNNING.");
6508 restart_events(megad
);
6511 if (ret
& PROMPT_RET_ENTER
) {
6512 // Back to the prompt only in stopped mode.
6513 if (events
== STOPPED_PROMPT
)
6515 if ((ret
& PROMPT_RET_MSG
) == 0)
6517 restart_events(megad
);
6523 case STOPPED_GAME_GENIE
:
6524 if (manage_game_genie(megad
, ksym
, ksym_uni
) == 0)
6526 if (events
== STOPPED_GAME_GENIE
) {
6527 // Return to stopped mode.
6528 pd_message(stopped_str
);
6532 restart_events(megad
);
6535 // In basic stopped mode, handle a few keysyms.
6536 if (ksym
== dgen_game_genie
[0]) {
6537 pd_message_cursor(strlen(game_genie_str
),
6539 events
= STOPPED_GAME_GENIE
;
6541 else if (ksym
== dgen_prompt
[0]) {
6542 pd_message_cursor(strlen(prompt_str
),
6544 events
= STOPPED_PROMPT
;
6546 else if (ksym
== dgen_quit
[0]) {
6547 restart_events(megad
);
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
);
6562 for (struct ctl
* ctl
= control
; (ctl
->rc
!= NULL
); ++ctl
) {
6563 if (ksym
!= (*ctl
->rc
)[RCBK
])
6565 assert(ctl
->press
!= NULL
);
6566 ctl
->pressed
= true;
6568 if (ctl
->press(*ctl
, megad
) == 0)
6571 if (manage_bindings(megad
, true, RCBK
, ksym
) == 0)
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
)))
6580 kpress
[(ksym
& 0xff)] = 0;
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
));
6592 if (events
!= STARTED
)
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
))
6600 ctl
->pressed
= false;
6602 if ((ctl
->release
!= NULL
) &&
6603 (ctl
->release(*ctl
, megad
) == 0))
6606 if (manage_bindings(megad
, false, RCBK
, ksym
) == 0)
6609 case SDL_MOUSEMOTION
:
6610 if (!mouse_is_grabbed()) {
6611 // Only show mouse pointer for a few seconds.
6613 hide_mouse_when
= (pd_usecs() + MOUSE_SHOW_USECS
);
6618 which
= event
.motion
.which
;
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');
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');
6642 rlist
[ri
++] = MO_MOTION(which
, 'd');
6643 rlist
[ri
++] = MO_MOTION(which
, 'u');
6646 mouse_motion_delay_release(which
, true);
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
]);
6654 for (i
= 0; ((calibrating
) && (i
!= pi
)); ++i
)
6655 manage_calibration(RCBM
, plist
[i
]);
6658 if (events
!= STARTED
)
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
]))
6666 ctl
->pressed
= false;
6668 ctl
->x
= event
.motion
.x
;
6669 ctl
->y
= event
.motion
.y
;
6670 if ((ctl
->release
!= NULL
) &&
6671 (ctl
->release(*ctl
, megad
) == 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;
6679 ctl
->x
= event
.motion
.x
;
6680 ctl
->y
= event
.motion
.y
;
6681 if (ctl
->press(*ctl
, megad
) == 0)
6686 for (i
= 0; (i
!= ri
); ++i
)
6687 if (!manage_bindings(megad
, false, RCBM
, rlist
[i
]))
6689 for (i
= 0; (i
!= pi
); ++i
)
6690 if (!manage_bindings(megad
, true, RCBM
, plist
[i
]))
6693 case SDL_MOUSEBUTTONDOWN
:
6694 assert(event
.button
.state
== SDL_PRESSED
);
6695 #ifdef WITH_DEBUGGER
6701 case SDL_MOUSEBUTTONUP
:
6702 assert(event
.button
.state
== SDL_RELEASED
);
6705 mouse
= MO_BUTTON(event
.button
.which
, event
.button
.button
);
6706 manage_combos(megad
, pressed
, RCBM
, mouse
);
6709 manage_calibration(RCBM
, mouse
);
6712 if (events
!= STARTED
)
6714 for (struct ctl
* ctl
= control
; (ctl
->rc
!= NULL
); ++ctl
) {
6715 if ((*ctl
->rc
)[RCBM
] != mouse
)
6717 ctl
->pressed
= pressed
;
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))
6727 assert(ctl
->press
!= NULL
);
6728 if (ctl
->press(*ctl
, megad
) == 0)
6732 if (manage_bindings(megad
, pressed
, RCBM
, mouse
) == 0)
6735 case SDL_VIDEORESIZE
:
6736 switch (screen_init(event
.resize
.w
, event
.resize
.h
)) {
6738 pd_message("Video resized to %ux%u.",
6743 pd_message("Failed to resize video to %ux%u.",
6749 "sdl: fatal error while trying to change screen"
6755 // We've been politely asked to exit, so let's leave
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
)));
6771 memset(buf
, 0x00, (screen
.pitch
* screen
.info_height
));
6774 ret
= font_text(buf
, screen
.width
, screen
.info_height
,
6775 screen
.Bpp
, screen
.pitch
, msg
, len
,
6776 mark
, FONT_TYPE_AUTO
);
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
);
6792 info
.since
= pd_usecs();
6798 * Process status bar message.
6800 static void pd_message_process(void)
6802 size_t len
= info
.length
;
6810 for (n
= 0; (n
< len
); ++n
)
6811 if (info
.message
[n
] == '\n') {
6815 r
= pd_message_display(info
.message
, n
, ~0u, false);
6818 memmove(info
.message
, &(info
.message
[len
]), (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
);
6834 * Write a message to the status bar.
6836 void pd_message(const char *msg
, ...)
6841 vsnprintf(info
.message
, sizeof(info
.message
), msg
, 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
)
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
)
6870 pd_message_postpone("\n");
6871 for (i
= 0; (i
< (sizeof(data
) / sizeof(data
[0]))); ++i
) {
6875 k
= (size_t)snprintf(buf
, sizeof(buf
), "%s: ", data
[i
].p
);
6876 if (k
>= (sizeof(buf
) - 1))
6878 // Filter out extra spaces.
6879 for (j
= 0; (j
< data
[i
].len
); ++j
)
6880 if (isgraph(data
[i
].s
[j
]))
6882 if (j
== data
[i
].len
)
6884 while ((j
< data
[i
].len
) && (k
< (sizeof(buf
) - 2))) {
6885 if (isgraph(data
[i
].s
[j
])) {
6886 buf
[(k
++)] = data
[i
].s
[j
];
6891 while ((j
< data
[i
].len
) && (!isgraph(data
[i
].s
[j
])))
6894 if (buf
[(k
- 1)] == ' ')
6897 buf
[(k
+ 1)] = '\0';
6898 pd_message_postpone(buf
);
6902 /* Clean up this awful mess :) */
6908 screen_update_thread_stop();
6911 free((void*)mdscr
.data
);
6914 SDL_QuitSubSystem(SDL_INIT_VIDEO
| SDL_INIT_AUDIO
);
6919 release_texture(screen
.texture
);
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;