main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
#define SDL_MAIN_USE_CALLBACKS 1
|
2023-08-02 10:13:35 -06:00
|
|
|
#include <SDL3/SDL_test.h>
|
|
|
|
#include <SDL3/SDL_test_common.h>
|
|
|
|
#include <SDL3/SDL_main.h>
|
|
|
|
#include "testutils.h"
|
|
|
|
|
|
|
|
#define POOF_LIFETIME 250
|
2023-09-07 08:56:09 -06:00
|
|
|
#define VISUALIZER_WIDTH 100
|
|
|
|
#define VISUALIZER_HEIGHT 50
|
2023-08-02 10:13:35 -06:00
|
|
|
|
|
|
|
typedef struct Texture
|
|
|
|
{
|
|
|
|
SDL_Texture *texture;
|
|
|
|
float w;
|
|
|
|
float h;
|
|
|
|
} Texture;
|
|
|
|
|
|
|
|
typedef enum ThingType
|
|
|
|
{
|
|
|
|
THING_NULL,
|
|
|
|
THING_PHYSDEV,
|
|
|
|
THING_PHYSDEV_CAPTURE,
|
|
|
|
THING_LOGDEV,
|
|
|
|
THING_LOGDEV_CAPTURE,
|
|
|
|
THING_TRASHCAN,
|
|
|
|
THING_STREAM,
|
|
|
|
THING_POOF,
|
|
|
|
THING_WAV
|
|
|
|
} ThingType;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct Thing Thing;
|
|
|
|
|
|
|
|
struct Thing
|
|
|
|
{
|
|
|
|
ThingType what;
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
SDL_AudioDeviceID devid;
|
|
|
|
SDL_bool iscapture;
|
|
|
|
SDL_AudioSpec spec;
|
|
|
|
char *name;
|
|
|
|
} physdev;
|
|
|
|
struct {
|
|
|
|
SDL_AudioDeviceID devid;
|
|
|
|
SDL_bool iscapture;
|
|
|
|
SDL_AudioSpec spec;
|
|
|
|
Thing *physdev;
|
2023-09-07 08:56:09 -06:00
|
|
|
SDL_bool visualizer_enabled;
|
|
|
|
SDL_bool visualizer_updated;
|
|
|
|
SDL_Texture *visualizer;
|
|
|
|
SDL_Mutex *postmix_lock;
|
|
|
|
float *postmix_buffer;
|
|
|
|
int postmix_buflen;
|
|
|
|
int postmix_allocated;
|
|
|
|
SDL_AudioSpec postmix_spec;
|
|
|
|
SDL_AtomicInt postmix_updated;
|
2023-08-02 10:13:35 -06:00
|
|
|
} logdev;
|
|
|
|
struct {
|
|
|
|
SDL_AudioSpec spec;
|
|
|
|
Uint8 *buf;
|
|
|
|
Uint32 buflen;
|
|
|
|
} wav;
|
|
|
|
struct {
|
|
|
|
float startw;
|
|
|
|
float starth;
|
|
|
|
float centerx;
|
|
|
|
float centery;
|
|
|
|
} poof;
|
|
|
|
struct {
|
|
|
|
SDL_AudioStream *stream;
|
2023-10-16 11:56:43 -06:00
|
|
|
int total_bytes;
|
2023-08-02 10:13:35 -06:00
|
|
|
Uint64 next_level_update;
|
|
|
|
Uint8 levels[5];
|
|
|
|
} stream;
|
|
|
|
} data;
|
|
|
|
|
|
|
|
Thing *line_connected_to;
|
|
|
|
char *titlebar;
|
|
|
|
SDL_FRect rect;
|
|
|
|
float z;
|
|
|
|
Uint8 r, g, b, a;
|
|
|
|
float progress;
|
|
|
|
float scale;
|
|
|
|
Uint64 createticks;
|
|
|
|
Texture *texture;
|
|
|
|
const ThingType *can_be_dropped_onto;
|
|
|
|
|
|
|
|
void (*ontick)(Thing *thing, Uint64 now);
|
|
|
|
void (*ondrag)(Thing *thing, int button, float x, float y);
|
|
|
|
void (*ondrop)(Thing *thing, int button, float x, float y);
|
|
|
|
void (*ondraw)(Thing *thing, SDL_Renderer *renderer);
|
|
|
|
|
|
|
|
Thing *prev;
|
|
|
|
Thing *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static Uint64 app_ready_ticks = 0;
|
|
|
|
static SDLTest_CommonState *state = NULL;
|
|
|
|
|
|
|
|
static Thing *things = NULL;
|
|
|
|
static char *current_titlebar = NULL;
|
|
|
|
|
2023-09-07 08:56:09 -06:00
|
|
|
static Thing *mouseover_thing = NULL;
|
2023-08-02 10:13:35 -06:00
|
|
|
static Thing *droppable_highlighted_thing = NULL;
|
|
|
|
static Thing *dragging_thing = NULL;
|
|
|
|
static int dragging_button = -1;
|
2023-10-11 08:01:52 -06:00
|
|
|
static int dragging_button_real = -1;
|
|
|
|
static SDL_bool ctrl_held = SDL_FALSE;
|
|
|
|
static SDL_bool alt_held = SDL_FALSE;
|
2023-08-02 10:13:35 -06:00
|
|
|
|
|
|
|
static Texture *physdev_texture = NULL;
|
|
|
|
static Texture *logdev_texture = NULL;
|
|
|
|
static Texture *audio_texture = NULL;
|
|
|
|
static Texture *trashcan_texture = NULL;
|
|
|
|
static Texture *soundboard_texture = NULL;
|
|
|
|
static Texture *soundboard_levels_texture = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static void SetTitleBar(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
char *newstr = NULL;
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
SDL_vasprintf(&newstr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (newstr && (!current_titlebar || (SDL_strcmp(current_titlebar, newstr) != 0))) {
|
|
|
|
SDL_SetWindowTitle(state->windows[0], newstr);
|
|
|
|
SDL_free(current_titlebar);
|
|
|
|
current_titlebar = newstr;
|
|
|
|
} else {
|
|
|
|
SDL_free(newstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetDefaultTitleBar(void)
|
|
|
|
{
|
|
|
|
SetTitleBar("testaudio: %s", SDL_GetCurrentAudioDriver());
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *FindThingAtPoint(const float x, const float y)
|
|
|
|
{
|
|
|
|
const SDL_FPoint pt = { x, y };
|
|
|
|
Thing *retval = NULL;
|
|
|
|
Thing *i;
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = i->next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
if ((i != dragging_thing) && SDL_PointInRectFloat(&pt, &i->rect)) {
|
|
|
|
retval = i; /* keep going, though, because things drawn on top are later in the list. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *UpdateMouseOver(const float x, const float y)
|
|
|
|
{
|
|
|
|
Thing *thing;
|
|
|
|
|
|
|
|
if (dragging_thing) {
|
|
|
|
thing = dragging_thing;
|
|
|
|
} else {
|
|
|
|
thing = FindThingAtPoint(x, y);
|
|
|
|
}
|
|
|
|
|
2023-09-20 14:57:37 -06:00
|
|
|
mouseover_thing = thing;
|
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
if (!thing) {
|
|
|
|
SetDefaultTitleBar();
|
|
|
|
} else if (thing->titlebar) {
|
|
|
|
SetTitleBar("%s", thing->titlebar);
|
|
|
|
}
|
|
|
|
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
2023-09-07 08:50:11 -06:00
|
|
|
static Thing *CreateThing(ThingType what, float x, float y, float z, float w, float h, Texture *texture, const char *titlebar)
|
2023-08-02 10:13:35 -06:00
|
|
|
{
|
|
|
|
Thing *last = NULL;
|
|
|
|
Thing *i;
|
|
|
|
Thing *thing;
|
|
|
|
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
thing = (Thing *) SDL_calloc(1, sizeof (Thing));
|
|
|
|
if (!thing) {
|
|
|
|
SDL_Log("Out of memory!");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
if ((w < 0) || (h < 0)) {
|
2023-11-11 02:28:24 -07:00
|
|
|
SDL_assert(texture != NULL);
|
2023-08-02 10:13:35 -06:00
|
|
|
if (w < 0) {
|
|
|
|
w = texture->w;
|
|
|
|
}
|
|
|
|
if (h < 0) {
|
|
|
|
h = texture->h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
thing->what = what;
|
|
|
|
thing->rect.x = x;
|
|
|
|
thing->rect.y = y;
|
|
|
|
thing->rect.w = w;
|
|
|
|
thing->rect.h = h;
|
|
|
|
thing->z = z;
|
|
|
|
thing->r = 255;
|
|
|
|
thing->g = 255;
|
|
|
|
thing->b = 255;
|
|
|
|
thing->a = 255;
|
|
|
|
thing->scale = 1.0f;
|
|
|
|
thing->createticks = SDL_GetTicks();
|
|
|
|
thing->texture = texture;
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
thing->titlebar = titlebar ? SDL_strdup(titlebar) : NULL; /* if allocation fails, oh well. */
|
2023-08-02 10:13:35 -06:00
|
|
|
|
|
|
|
/* insert in list by Z order (furthest from the "camera" first, so they get drawn over; negative Z is not drawn at all). */
|
2023-11-09 14:29:15 -07:00
|
|
|
if (!things) {
|
2023-08-02 10:13:35 -06:00
|
|
|
things = thing;
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = i->next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
if (z > i->z) { /* insert here. */
|
|
|
|
thing->next = i;
|
|
|
|
thing->prev = i->prev;
|
|
|
|
|
|
|
|
SDL_assert(i->prev == last);
|
|
|
|
|
|
|
|
if (i->prev) {
|
|
|
|
i->prev->next = thing;
|
|
|
|
} else {
|
|
|
|
SDL_assert(i == things);
|
|
|
|
things = thing;
|
|
|
|
}
|
|
|
|
i->prev = thing;
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
last = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last) {
|
|
|
|
last->next = thing;
|
|
|
|
thing->prev = last;
|
|
|
|
}
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DestroyThing(Thing *thing)
|
|
|
|
{
|
|
|
|
if (!thing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-07 08:56:09 -06:00
|
|
|
if (mouseover_thing == thing) {
|
|
|
|
mouseover_thing = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (droppable_highlighted_thing == thing) {
|
|
|
|
droppable_highlighted_thing = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dragging_thing == thing) {
|
|
|
|
dragging_thing = NULL;
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
switch (thing->what) {
|
|
|
|
case THING_POOF: break;
|
|
|
|
case THING_NULL: break;
|
|
|
|
case THING_TRASHCAN: break;
|
2023-09-07 08:56:09 -06:00
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
case THING_LOGDEV:
|
|
|
|
case THING_LOGDEV_CAPTURE:
|
|
|
|
SDL_CloseAudioDevice(thing->data.logdev.devid);
|
2023-10-16 08:03:42 -06:00
|
|
|
if (state->renderers[0] != NULL) {
|
|
|
|
SDL_DestroyTexture(thing->data.logdev.visualizer);
|
|
|
|
}
|
2023-09-07 08:56:09 -06:00
|
|
|
SDL_DestroyMutex(thing->data.logdev.postmix_lock);
|
|
|
|
SDL_free(thing->data.logdev.postmix_buffer);
|
2023-08-02 10:13:35 -06:00
|
|
|
break;
|
2023-09-07 08:56:09 -06:00
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
case THING_PHYSDEV:
|
|
|
|
case THING_PHYSDEV_CAPTURE:
|
|
|
|
SDL_free(thing->data.physdev.name);
|
|
|
|
break;
|
2023-09-07 08:56:09 -06:00
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
case THING_WAV:
|
|
|
|
SDL_free(thing->data.wav.buf);
|
|
|
|
break;
|
2023-09-07 08:56:09 -06:00
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
case THING_STREAM:
|
|
|
|
SDL_DestroyAudioStream(thing->data.stream.stream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->prev) {
|
|
|
|
thing->prev->next = thing->next;
|
|
|
|
} else {
|
|
|
|
SDL_assert(thing == things);
|
|
|
|
things = thing->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->next) {
|
|
|
|
thing->next->prev = thing->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_free(thing->titlebar);
|
|
|
|
SDL_free(thing);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DrawOneThing(SDL_Renderer *renderer, Thing *thing)
|
|
|
|
{
|
|
|
|
SDL_FRect dst;
|
|
|
|
SDL_memcpy(&dst, &thing->rect, sizeof (SDL_FRect));
|
|
|
|
if (thing->scale != 1.0f) {
|
|
|
|
const float centerx = thing->rect.x + (thing->rect.w / 2);
|
|
|
|
const float centery = thing->rect.y + (thing->rect.h / 2);
|
2023-10-30 11:09:56 -06:00
|
|
|
const int w = thing->texture ? (int) thing->texture->w : 128;
|
|
|
|
const int h = thing->texture ? (int) thing->texture->h : 128;
|
2023-10-16 12:03:58 -06:00
|
|
|
dst.w = w * thing->scale;
|
|
|
|
dst.h = h * thing->scale;
|
2023-08-02 10:13:35 -06:00
|
|
|
dst.x = centerx - (dst.w / 2);
|
|
|
|
dst.y = centery - (dst.h / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->texture) {
|
|
|
|
if (droppable_highlighted_thing == thing) {
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 100);
|
|
|
|
SDL_RenderFillRect(renderer, &dst);
|
|
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a);
|
|
|
|
SDL_RenderTexture(renderer, thing->texture->texture, NULL, &dst);
|
|
|
|
} else {
|
|
|
|
SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a);
|
|
|
|
SDL_RenderFillRect(renderer, &dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->ondraw) {
|
|
|
|
thing->ondraw(thing, renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->progress > 0.0f) {
|
|
|
|
SDL_FRect r = { thing->rect.x, thing->rect.y + (thing->rect.h + 2.0f), 0.0f, 10.0f };
|
|
|
|
r.w = thing->rect.w * ((thing->progress > 1.0f) ? 1.0f : thing->progress);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128);
|
|
|
|
SDL_RenderFillRect(renderer, &r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DrawThings(SDL_Renderer *renderer)
|
|
|
|
{
|
|
|
|
Thing *i;
|
|
|
|
|
|
|
|
/* draw connecting lines first, so they're behind everything else. */
|
|
|
|
for (i = things; i && (i->z >= 0.0f); i = i->next) {
|
|
|
|
Thing *dst = i->line_connected_to;
|
|
|
|
if (dst) {
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
|
|
|
|
SDL_RenderLine(renderer, i->rect.x + (i->rect.w / 2), i->rect.y + (i->rect.h / 2), dst->rect.x + (dst->rect.w / 2), dst->rect.y + (dst->rect.h / 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw the actual things. */
|
|
|
|
for (i = things; i && (i->z >= 0.0f); i = i->next) {
|
|
|
|
if (i != dragging_thing) {
|
|
|
|
DrawOneThing(renderer, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dragging_thing) {
|
|
|
|
DrawOneThing(renderer, dragging_thing); /* draw last so it's always on top. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Draw(void)
|
|
|
|
{
|
|
|
|
SDL_Renderer *renderer = state->renderers[0];
|
2023-10-14 11:43:22 -06:00
|
|
|
if (renderer) { /* might be NULL if we're shutting down. */
|
|
|
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 64, 0, 64, 255);
|
|
|
|
SDL_RenderClear(renderer);
|
|
|
|
DrawThings(renderer);
|
|
|
|
SDL_RenderPresent(renderer);
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RepositionRowOfThings(const ThingType what, const float y)
|
|
|
|
{
|
|
|
|
int total_things = 0;
|
|
|
|
float texw = 0.0f;
|
|
|
|
float texh = 0.0f;
|
|
|
|
Thing *i;
|
|
|
|
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = i->next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
if (i->what == what) {
|
|
|
|
texw = i->rect.w;
|
|
|
|
texh = i->rect.h;
|
|
|
|
total_things++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (total_things > 0) {
|
|
|
|
int w, h;
|
|
|
|
SDL_GetWindowSize(state->windows[0], &w, &h);
|
|
|
|
const float spacing = w / ((float) total_things);
|
|
|
|
float x = (spacing - texw) / 2.0f;
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = i->next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
if (i->what == what) {
|
|
|
|
i->rect.x = x;
|
|
|
|
i->rect.y = (y >= 0.0f) ? y : ((h + y) - texh);
|
|
|
|
x += spacing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *AudioFmtToString(const SDL_AudioFormat fmt)
|
|
|
|
{
|
|
|
|
switch (fmt) {
|
|
|
|
#define FMTCASE(x) case SDL_AUDIO_##x: return #x
|
|
|
|
FMTCASE(U8);
|
|
|
|
FMTCASE(S8);
|
2023-09-04 10:17:29 -06:00
|
|
|
FMTCASE(S16LE);
|
|
|
|
FMTCASE(S16BE);
|
|
|
|
FMTCASE(S32LE);
|
|
|
|
FMTCASE(S32BE);
|
|
|
|
FMTCASE(F32LE);
|
|
|
|
FMTCASE(F32BE);
|
2023-08-02 10:13:35 -06:00
|
|
|
#undef FMTCASE
|
|
|
|
}
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *AudioChansToStr(const int channels)
|
|
|
|
{
|
|
|
|
switch (channels) {
|
|
|
|
case 1: return "mono";
|
|
|
|
case 2: return "stereo";
|
|
|
|
case 3: return "2.1";
|
|
|
|
case 4: return "quad";
|
|
|
|
case 5: return "4.1";
|
|
|
|
case 6: return "5.1";
|
|
|
|
case 7: return "6.1";
|
|
|
|
case 8: return "7.1";
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PoofThing_ondrag(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
dragging_thing = NULL; /* refuse to be dragged. */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PoofThing_ontick(Thing *thing, Uint64 now)
|
|
|
|
{
|
|
|
|
const int lifetime = POOF_LIFETIME;
|
|
|
|
const int elasped = (int) (now - thing->createticks);
|
|
|
|
if (elasped > lifetime) {
|
|
|
|
DestroyThing(thing);
|
|
|
|
} else {
|
|
|
|
const float pct = ((float) elasped) / ((float) lifetime);
|
|
|
|
thing->a = (Uint8) (int) (255.0f - (pct * 255.0f));
|
|
|
|
thing->scale = 1.0f - pct; /* shrink to nothing! */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *CreatePoofThing(Thing *poofing_thing)
|
|
|
|
{
|
|
|
|
const float centerx = poofing_thing->rect.x + (poofing_thing->rect.w / 2);
|
|
|
|
const float centery = poofing_thing->rect.y + (poofing_thing->rect.h / 2);
|
|
|
|
const float z = poofing_thing->z;
|
|
|
|
Thing *thing = CreateThing(THING_POOF, poofing_thing->rect.x, poofing_thing->rect.y, z, poofing_thing->rect.w, poofing_thing->rect.h, poofing_thing->texture, NULL);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (thing) {
|
|
|
|
thing->data.poof.startw = poofing_thing->rect.w;
|
|
|
|
thing->data.poof.starth = poofing_thing->rect.h;
|
|
|
|
thing->data.poof.centerx = centerx;
|
|
|
|
thing->data.poof.centery = centery;
|
|
|
|
thing->ontick = PoofThing_ontick;
|
|
|
|
thing->ondrag = PoofThing_ondrag;
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DestroyThingInPoof(Thing *thing)
|
|
|
|
{
|
|
|
|
if (thing) {
|
|
|
|
if (thing->what != THING_POOF) {
|
|
|
|
CreatePoofThing(thing);
|
|
|
|
}
|
|
|
|
DestroyThing(thing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this poofs a thing and additionally poofs all things connected to the thing. */
|
|
|
|
static void TrashThing(Thing *thing)
|
|
|
|
{
|
|
|
|
Thing *i, *next;
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
next = i->next;
|
|
|
|
if (i->line_connected_to == thing) {
|
|
|
|
TrashThing(i);
|
|
|
|
next = things; /* start over in case this blew up the list. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DestroyThingInPoof(thing);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void StreamThing_ontick(Thing *thing, Uint64 now)
|
|
|
|
{
|
|
|
|
if (!thing->line_connected_to) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* are we playing? See if we're done, or update state. */
|
|
|
|
if (thing->line_connected_to->what == THING_LOGDEV) {
|
|
|
|
const int available = SDL_GetAudioStreamAvailable(thing->data.stream.stream);
|
2023-10-16 11:56:43 -06:00
|
|
|
if (!available) {
|
2023-08-02 10:13:35 -06:00
|
|
|
DestroyThingInPoof(thing);
|
2023-10-16 08:03:42 -06:00
|
|
|
return;
|
2023-08-02 10:13:35 -06:00
|
|
|
} else {
|
2023-10-16 11:56:43 -06:00
|
|
|
thing->progress = 1.0f - (thing->data.stream.total_bytes ? (((float) (available)) / ((float) thing->data.stream.total_bytes)) : 0.0f);
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->data.stream.next_level_update <= now) {
|
|
|
|
Uint64 perf = SDL_GetPerformanceCounter();
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < SDL_arraysize(thing->data.stream.levels); i++) {
|
|
|
|
thing->data.stream.levels[i] = (Uint8) (perf % 6);
|
|
|
|
perf >>= 3;
|
|
|
|
}
|
|
|
|
thing->data.stream.next_level_update += 150;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void StreamThing_ondrag(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (button == SDL_BUTTON_RIGHT) { /* this is kinda hacky, but use this to disconnect from a playing source. */
|
|
|
|
if (thing->line_connected_to) {
|
|
|
|
SDL_UnbindAudioStream(thing->data.stream.stream); /* unbind from current device */
|
2023-10-16 11:56:43 -06:00
|
|
|
if (thing->line_connected_to->what == THING_LOGDEV_CAPTURE) {
|
|
|
|
SDL_FlushAudioStream(thing->data.stream.stream);
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
thing->line_connected_to = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void StreamThing_ondrop(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (droppable_highlighted_thing) {
|
|
|
|
if (droppable_highlighted_thing->what == THING_TRASHCAN) {
|
|
|
|
TrashThing(thing);
|
|
|
|
} else if (((droppable_highlighted_thing->what == THING_LOGDEV) || (droppable_highlighted_thing->what == THING_LOGDEV_CAPTURE)) && (droppable_highlighted_thing != thing->line_connected_to)) {
|
|
|
|
/* connect to a logical device! */
|
2023-08-02 13:23:37 -06:00
|
|
|
SDL_Log("Binding audio stream ('%s') to logical device %u", thing->titlebar, (unsigned int) droppable_highlighted_thing->data.logdev.devid);
|
2023-08-02 10:13:35 -06:00
|
|
|
if (thing->line_connected_to) {
|
|
|
|
SDL_UnbindAudioStream(thing->data.stream.stream); /* unbind from current device */
|
|
|
|
if (thing->line_connected_to->what == THING_LOGDEV_CAPTURE) {
|
|
|
|
SDL_FlushAudioStream(thing->data.stream.stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_BindAudioStream(droppable_highlighted_thing->data.logdev.devid, thing->data.stream.stream); /* bind to new device! */
|
2023-10-16 11:56:43 -06:00
|
|
|
thing->data.stream.total_bytes = SDL_GetAudioStreamAvailable(thing->data.stream.stream);
|
2023-08-02 10:13:35 -06:00
|
|
|
thing->progress = 0.0f; /* ontick will adjust this if we're on an output device.*/
|
|
|
|
thing->data.stream.next_level_update = SDL_GetTicks() + 100;
|
|
|
|
thing->line_connected_to = droppable_highlighted_thing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void StreamThing_ondraw(Thing *thing, SDL_Renderer *renderer)
|
|
|
|
{
|
|
|
|
if (thing->line_connected_to) { /* are we playing? Update progress bar, and bounce the levels a little. */
|
|
|
|
static const float xlocs[5] = { 18, 39, 59, 79, 99 };
|
|
|
|
static const float ylocs[5] = { 49, 39, 29, 19, 10 };
|
|
|
|
const float blockw = soundboard_levels_texture->w;
|
|
|
|
const float blockh = soundboard_levels_texture->h / 5.0f;
|
|
|
|
int i, j;
|
|
|
|
SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a);
|
|
|
|
for (i = 0; i < SDL_arraysize(thing->data.stream.levels); i++) {
|
|
|
|
const int level = (int) thing->data.stream.levels[i];
|
|
|
|
const float x = xlocs[i];
|
|
|
|
for (j = 0; j < level; j++) {
|
|
|
|
const SDL_FRect src = { 0, soundboard_levels_texture->h - ((j+1) * blockh), blockw, blockh };
|
|
|
|
const SDL_FRect dst = { thing->rect.x + x, thing->rect.y + ylocs[j], blockw, blockh };
|
|
|
|
SDL_RenderTexture(renderer, soundboard_levels_texture->texture, &src, &dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *CreateStreamThing(const SDL_AudioSpec *spec, const Uint8 *buf, const Uint32 buflen, const char *fname, const float x, const float y)
|
|
|
|
{
|
|
|
|
static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_LOGDEV, THING_LOGDEV_CAPTURE, THING_NULL };
|
2023-09-07 08:50:11 -06:00
|
|
|
Thing *thing = CreateThing(THING_STREAM, x, y, 0, -1, -1, soundboard_texture, fname);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (thing) {
|
|
|
|
SDL_Log("Adding audio stream for %s", fname ? fname : "(null)");
|
|
|
|
thing->data.stream.stream = SDL_CreateAudioStream(spec, spec);
|
|
|
|
if (buf && buflen) {
|
|
|
|
SDL_PutAudioStreamData(thing->data.stream.stream, buf, (int) buflen);
|
|
|
|
SDL_FlushAudioStream(thing->data.stream.stream);
|
|
|
|
thing->data.stream.total_bytes = SDL_GetAudioStreamAvailable(thing->data.stream.stream);
|
|
|
|
}
|
|
|
|
thing->ontick = StreamThing_ontick;
|
|
|
|
thing->ondrag = StreamThing_ondrag;
|
|
|
|
thing->ondrop = StreamThing_ondrop;
|
|
|
|
thing->ondraw = StreamThing_ondraw;
|
|
|
|
thing->can_be_dropped_onto = can_be_dropped_onto;
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WavThing_ondrag(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (button == SDL_BUTTON_RIGHT) { /* drag out a new audio stream. */
|
|
|
|
dragging_thing = CreateStreamThing(&thing->data.wav.spec, thing->data.wav.buf, thing->data.wav.buflen, thing->titlebar, x - (thing->rect.w / 2), y - (thing->rect.h / 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WavThing_ondrop(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (droppable_highlighted_thing) {
|
|
|
|
if (droppable_highlighted_thing->what == THING_TRASHCAN) {
|
|
|
|
TrashThing(thing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *LoadWavThing(const char *fname, float x, float y)
|
|
|
|
{
|
|
|
|
Thing *thing = NULL;
|
|
|
|
char *path;
|
|
|
|
SDL_AudioSpec spec;
|
|
|
|
Uint8 *buf = NULL;
|
|
|
|
Uint32 buflen = 0;
|
|
|
|
|
|
|
|
path = GetNearbyFilename(fname);
|
|
|
|
if (path) {
|
|
|
|
fname = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_LoadWAV(fname, &spec, &buf, &buflen) == 0) {
|
|
|
|
static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL };
|
|
|
|
char *titlebar = NULL;
|
|
|
|
const char *nodirs = SDL_strrchr(fname, '/');
|
2024-01-23 18:40:51 -07:00
|
|
|
#ifdef SDL_PLATFORM_WINDOWS
|
2023-08-02 10:13:35 -06:00
|
|
|
const char *nodirs2 = SDL_strrchr(nodirs ? nodirs : fname, '\\');
|
|
|
|
if (nodirs2) {
|
|
|
|
nodirs = nodirs2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SDL_Log("Adding WAV file '%s'", fname);
|
|
|
|
|
|
|
|
if (nodirs) {
|
|
|
|
nodirs++;
|
|
|
|
} else {
|
|
|
|
nodirs = fname;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_asprintf(&titlebar, "WAV file (\"%s\", %s, %s, %uHz)", nodirs, AudioFmtToString(spec.format), AudioChansToStr(spec.channels), (unsigned int) spec.freq);
|
|
|
|
thing = CreateThing(THING_WAV, x - (audio_texture->w / 2), y - (audio_texture->h / 2), 5, -1, -1, audio_texture, titlebar);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (thing) {
|
|
|
|
SDL_free(titlebar);
|
|
|
|
SDL_memcpy(&thing->data.wav.spec, &spec, sizeof (SDL_AudioSpec));
|
|
|
|
thing->data.wav.buf = buf;
|
|
|
|
thing->data.wav.buflen = buflen;
|
|
|
|
thing->can_be_dropped_onto = can_be_dropped_onto;
|
|
|
|
thing->ondrag = WavThing_ondrag;
|
|
|
|
thing->ondrop = WavThing_ondrop;
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
SDL_free(path);
|
|
|
|
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *LoadStockWavThing(const char *fname)
|
|
|
|
{
|
|
|
|
char *path = GetNearbyFilename(fname);
|
|
|
|
Thing *thing = LoadWavThing(path ? path : fname, 0.0f, 0.0f); /* will reposition in a moment. */
|
|
|
|
SDL_free(path);
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LoadStockWavThings(void)
|
|
|
|
{
|
|
|
|
LoadStockWavThing("sample.wav");
|
|
|
|
RepositionRowOfThings(THING_WAV, -10.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DestroyTexture(Texture *tex)
|
|
|
|
{
|
|
|
|
if (tex) {
|
2023-10-14 21:17:59 -06:00
|
|
|
if (state->renderers[0] != NULL) { /* if the renderer went away, this pointer is already bogus. */
|
|
|
|
SDL_DestroyTexture(tex->texture);
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
SDL_free(tex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Texture *CreateTexture(const char *fname)
|
|
|
|
{
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
Texture *tex = (Texture *) SDL_calloc(1, sizeof (Texture));
|
|
|
|
if (!tex) {
|
|
|
|
SDL_Log("Out of memory!");
|
|
|
|
} else {
|
|
|
|
int texw, texh;
|
|
|
|
tex->texture = LoadTexture(state->renderers[0], fname, SDL_TRUE, &texw, &texh);
|
|
|
|
if (!tex->texture) {
|
|
|
|
SDL_Log("Failed to load '%s': %s", fname, SDL_GetError());
|
|
|
|
SDL_free(tex);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_BLEND);
|
|
|
|
tex->w = (float) texw;
|
|
|
|
tex->h = (float) texh;
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID which, const float x, const float y);
|
|
|
|
|
|
|
|
static void DeviceThing_ondrag(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if ((button == SDL_BUTTON_MIDDLE) && (thing->what == THING_LOGDEV_CAPTURE)) { /* drag out a new stream. This is a UX mess. :/ */
|
|
|
|
dragging_thing = CreateStreamThing(&thing->data.logdev.spec, NULL, 0, NULL, x, y);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (dragging_thing) {
|
|
|
|
dragging_thing->data.stream.next_level_update = SDL_GetTicks() + 100;
|
|
|
|
SDL_BindAudioStream(thing->data.logdev.devid, dragging_thing->data.stream.stream); /* bind to new device! */
|
|
|
|
dragging_thing->line_connected_to = thing;
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
} else if (button == SDL_BUTTON_RIGHT) { /* drag out a new logical device. */
|
|
|
|
const SDL_AudioDeviceID which = ((thing->what == THING_LOGDEV) || (thing->what == THING_LOGDEV_CAPTURE)) ? thing->data.logdev.devid : thing->data.physdev.devid;
|
|
|
|
const SDL_AudioDeviceID devid = SDL_OpenAudioDevice(which, NULL);
|
|
|
|
dragging_thing = devid ? CreateLogicalDeviceThing(thing, devid, x - (thing->rect.w / 2), y - (thing->rect.h / 2)) : NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetLogicalDeviceTitlebar(Thing *thing)
|
|
|
|
{
|
|
|
|
SDL_AudioSpec *spec = &thing->data.logdev.spec;
|
2023-09-13 09:03:17 -06:00
|
|
|
int frames = 0;
|
|
|
|
SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec, &frames);
|
2023-09-07 08:50:11 -06:00
|
|
|
SDL_free(thing->titlebar);
|
2023-09-13 09:03:17 -06:00
|
|
|
SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz, %d frames)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.iscapture ? "CAPTURE" : "OUTPUT", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void LogicalDeviceThing_ondrop(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (droppable_highlighted_thing) {
|
|
|
|
if (droppable_highlighted_thing->what == THING_TRASHCAN) {
|
|
|
|
TrashThing(thing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-07 08:56:09 -06:00
|
|
|
static void SDLCALL PostmixCallback(void *userdata, const SDL_AudioSpec *spec, float *buffer, int buflen)
|
|
|
|
{
|
|
|
|
Thing *thing = (Thing *) userdata;
|
|
|
|
|
|
|
|
SDL_LockMutex(thing->data.logdev.postmix_lock);
|
|
|
|
|
|
|
|
if (thing->data.logdev.postmix_allocated < buflen) {
|
|
|
|
void *ptr = SDL_realloc(thing->data.logdev.postmix_buffer, buflen);
|
|
|
|
if (!ptr) {
|
|
|
|
SDL_UnlockMutex(thing->data.logdev.postmix_lock);
|
|
|
|
return; /* oh well. */
|
|
|
|
}
|
|
|
|
thing->data.logdev.postmix_buffer = (float *) ptr;
|
|
|
|
thing->data.logdev.postmix_allocated = buflen;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_copyp(&thing->data.logdev.postmix_spec, spec);
|
|
|
|
SDL_memcpy(thing->data.logdev.postmix_buffer, buffer, buflen);
|
|
|
|
thing->data.logdev.postmix_buflen = buflen;
|
|
|
|
SDL_AtomicSet(&thing->data.logdev.postmix_updated, 1);
|
|
|
|
|
|
|
|
SDL_UnlockMutex(thing->data.logdev.postmix_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateVisualizer(SDL_Renderer *renderer, SDL_Texture *visualizer, const int channels, const float *buffer, const int buflen)
|
|
|
|
{
|
|
|
|
static const SDL_Color channel_colors[8] = {
|
|
|
|
{ 255, 255, 255, 255 },
|
|
|
|
{ 255, 0, 0, 255 },
|
|
|
|
{ 0, 255, 0, 255 },
|
|
|
|
{ 0, 0, 255, 255 },
|
|
|
|
{ 255, 255, 0, 255 },
|
|
|
|
{ 0, 255, 255, 255 },
|
|
|
|
{ 255, 0, 255, 255 },
|
|
|
|
{ 127, 127, 127, 255 }
|
|
|
|
};
|
|
|
|
|
|
|
|
SDL_SetRenderTarget(renderer, visualizer);
|
|
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
|
|
|
SDL_RenderClear(renderer);
|
|
|
|
|
|
|
|
if (buffer && buflen) {
|
|
|
|
const int frames = (buflen / sizeof (float)) / channels;
|
|
|
|
const int skip = frames / (VISUALIZER_WIDTH * 2);
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = channels - 1; i >= 0; i--) {
|
|
|
|
const SDL_Color *color = &channel_colors[i % SDL_arraysize(channel_colors)];
|
|
|
|
SDL_FPoint points[VISUALIZER_WIDTH + 2];
|
|
|
|
float prevx = 0.0f;
|
|
|
|
int pointidx = 1;
|
|
|
|
|
|
|
|
points[0].x = 0.0f;
|
|
|
|
points[0].y = VISUALIZER_HEIGHT * 0.5f;
|
|
|
|
|
|
|
|
for (j = 0; j < (SDL_arraysize(points)-1); j++) {
|
|
|
|
const float val = buffer[((j * skip) * channels) + i];
|
|
|
|
const float x = prevx + 2;
|
|
|
|
const float y = (VISUALIZER_HEIGHT * 0.5f) - (VISUALIZER_HEIGHT * (val * 0.5f));
|
|
|
|
SDL_assert(pointidx < SDL_arraysize(points));
|
|
|
|
points[pointidx].x = x;
|
|
|
|
points[pointidx].y = y;
|
|
|
|
pointidx++;
|
|
|
|
prevx = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, 255);
|
|
|
|
SDL_RenderLines(renderer, points, pointidx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SetRenderTarget(renderer, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LogicalDeviceThing_ontick(Thing *thing, Uint64 now)
|
|
|
|
{
|
2023-11-03 10:27:29 -06:00
|
|
|
const SDL_bool ismousedover = (thing == mouseover_thing);
|
2023-09-07 08:56:09 -06:00
|
|
|
|
|
|
|
if (!thing->data.logdev.visualizer || !thing->data.logdev.postmix_lock) { /* need these to work, skip if they failed. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->data.logdev.visualizer_enabled != ismousedover) {
|
|
|
|
thing->data.logdev.visualizer_enabled = ismousedover;
|
|
|
|
if (!ismousedover) {
|
|
|
|
SDL_SetAudioPostmixCallback(thing->data.logdev.devid, NULL, NULL);
|
|
|
|
} else {
|
|
|
|
if (thing->data.logdev.postmix_buffer) {
|
|
|
|
SDL_memset(thing->data.logdev.postmix_buffer, '\0', thing->data.logdev.postmix_buflen);
|
|
|
|
}
|
|
|
|
SDL_AtomicSet(&thing->data.logdev.postmix_updated, 1); /* so this will at least clear the texture later. */
|
|
|
|
SDL_SetAudioPostmixCallback(thing->data.logdev.devid, PostmixCallback, thing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LogicalDeviceThing_ondraw(Thing *thing, SDL_Renderer *renderer)
|
|
|
|
{
|
|
|
|
if (thing->data.logdev.visualizer_enabled) {
|
|
|
|
SDL_FRect dst;
|
|
|
|
dst.w = thing->rect.w;
|
|
|
|
dst.h = thing->rect.h;
|
|
|
|
dst.x = thing->rect.x + ((thing->rect.w - dst.w) / 2);
|
|
|
|
dst.y = thing->rect.y + ((thing->rect.h - dst.h) / 2);
|
|
|
|
|
|
|
|
if (SDL_AtomicGet(&thing->data.logdev.postmix_updated)) {
|
|
|
|
float *buffer;
|
|
|
|
int channels;
|
|
|
|
int buflen;
|
|
|
|
|
|
|
|
SDL_LockMutex(thing->data.logdev.postmix_lock);
|
|
|
|
channels = thing->data.logdev.postmix_spec.channels;
|
|
|
|
buflen = thing->data.logdev.postmix_buflen;
|
|
|
|
buffer = (float *) SDL_malloc(thing->data.logdev.postmix_buflen);
|
|
|
|
if (buffer) {
|
|
|
|
SDL_memcpy(buffer, thing->data.logdev.postmix_buffer, thing->data.logdev.postmix_buflen);
|
|
|
|
SDL_AtomicSet(&thing->data.logdev.postmix_updated, 0);
|
|
|
|
}
|
|
|
|
SDL_UnlockMutex(thing->data.logdev.postmix_lock);
|
|
|
|
|
|
|
|
UpdateVisualizer(renderer, thing->data.logdev.visualizer, channels, buffer, buflen);
|
|
|
|
SDL_free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 30);
|
|
|
|
SDL_RenderTexture(renderer, thing->data.logdev.visualizer, NULL, &dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID which, const float x, const float y)
|
|
|
|
{
|
|
|
|
static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL };
|
|
|
|
Thing *physthing = ((parent->what == THING_LOGDEV) || (parent->what == THING_LOGDEV_CAPTURE)) ? parent->data.logdev.physdev : parent;
|
|
|
|
const SDL_bool iscapture = physthing->data.physdev.iscapture;
|
|
|
|
Thing *thing;
|
|
|
|
|
|
|
|
SDL_Log("Adding logical audio device %u", (unsigned int) which);
|
|
|
|
thing = CreateThing(iscapture ? THING_LOGDEV_CAPTURE : THING_LOGDEV, x, y, 5, -1, -1, logdev_texture, NULL);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (thing) {
|
|
|
|
thing->data.logdev.devid = which;
|
|
|
|
thing->data.logdev.iscapture = iscapture;
|
|
|
|
thing->data.logdev.physdev = physthing;
|
|
|
|
thing->data.logdev.visualizer = SDL_CreateTexture(state->renderers[0], SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, VISUALIZER_WIDTH, VISUALIZER_HEIGHT);
|
|
|
|
thing->data.logdev.postmix_lock = SDL_CreateMutex();
|
|
|
|
if (thing->data.logdev.visualizer) {
|
|
|
|
SDL_SetTextureBlendMode(thing->data.logdev.visualizer, SDL_BLENDMODE_BLEND);
|
|
|
|
}
|
|
|
|
thing->line_connected_to = physthing;
|
|
|
|
thing->ontick = LogicalDeviceThing_ontick;
|
|
|
|
thing->ondrag = DeviceThing_ondrag;
|
|
|
|
thing->ondrop = LogicalDeviceThing_ondrop;
|
|
|
|
thing->ondraw = LogicalDeviceThing_ondraw;
|
|
|
|
thing->can_be_dropped_onto = can_be_dropped_onto;
|
|
|
|
|
|
|
|
SetLogicalDeviceTitlebar(thing);
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetPhysicalDeviceTitlebar(Thing *thing)
|
|
|
|
{
|
2023-09-13 09:03:17 -06:00
|
|
|
int frames = 0;
|
2023-08-02 10:13:35 -06:00
|
|
|
SDL_AudioSpec *spec = &thing->data.physdev.spec;
|
2023-09-13 09:03:17 -06:00
|
|
|
SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec, &frames);
|
2023-09-07 08:50:11 -06:00
|
|
|
SDL_free(thing->titlebar);
|
2023-08-02 10:13:35 -06:00
|
|
|
if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
|
2023-09-13 09:03:17 -06:00
|
|
|
SDL_asprintf(&thing->titlebar, "Default system device (CAPTURE, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
|
2023-08-02 10:13:35 -06:00
|
|
|
} else if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
|
2023-09-13 09:03:17 -06:00
|
|
|
SDL_asprintf(&thing->titlebar, "Default system device (OUTPUT, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
|
2023-08-02 10:13:35 -06:00
|
|
|
} else {
|
2023-09-13 09:03:17 -06:00
|
|
|
SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz, %d frames)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.iscapture ? "CAPTURE" : "OUTPUT", thing->data.physdev.name, AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PhysicalDeviceThing_ondrop(Thing *thing, int button, float x, float y)
|
|
|
|
{
|
|
|
|
if (droppable_highlighted_thing) {
|
|
|
|
if (droppable_highlighted_thing->what == THING_TRASHCAN) {
|
|
|
|
TrashThing(thing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PhysicalDeviceThing_ontick(Thing *thing, Uint64 now)
|
|
|
|
{
|
|
|
|
const int lifetime = POOF_LIFETIME;
|
|
|
|
const int elasped = (int) (now - thing->createticks);
|
|
|
|
if (elasped > lifetime) {
|
|
|
|
thing->scale = 1.0f;
|
|
|
|
thing->a = 255;
|
|
|
|
thing->ontick = NULL; /* no more ticking. */
|
|
|
|
} else {
|
|
|
|
const float pct = ((float) elasped) / ((float) lifetime);
|
|
|
|
thing->a = (Uint8) (int) (pct * 255.0f);
|
|
|
|
thing->scale = pct; /* grow to normal size */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Thing *CreatePhysicalDeviceThing(const SDL_AudioDeviceID which, const SDL_bool iscapture)
|
|
|
|
{
|
|
|
|
static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL };
|
|
|
|
static float next_physdev_x = 0;
|
|
|
|
Thing *thing;
|
|
|
|
int winw, winh;
|
|
|
|
|
|
|
|
SDL_GetWindowSize(state->windows[0], &winw, &winh);
|
|
|
|
if (next_physdev_x > (winw-physdev_texture->w)) {
|
|
|
|
next_physdev_x = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_Log("Adding physical audio device %u", (unsigned int) which);
|
|
|
|
thing = CreateThing(iscapture ? THING_PHYSDEV_CAPTURE : THING_PHYSDEV, next_physdev_x, 170, 5, -1, -1, physdev_texture, NULL);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if (thing) {
|
|
|
|
thing->data.physdev.devid = which;
|
|
|
|
thing->data.physdev.iscapture = iscapture;
|
|
|
|
thing->data.physdev.name = SDL_GetAudioDeviceName(which);
|
|
|
|
thing->ondrag = DeviceThing_ondrag;
|
|
|
|
thing->ondrop = PhysicalDeviceThing_ondrop;
|
|
|
|
thing->ontick = PhysicalDeviceThing_ontick;
|
|
|
|
thing->can_be_dropped_onto = can_be_dropped_onto;
|
|
|
|
|
|
|
|
SetPhysicalDeviceTitlebar(thing);
|
|
|
|
if (SDL_GetTicks() <= (app_ready_ticks + 2000)) { /* assume this is the initial batch if it happens in the first two seconds. */
|
|
|
|
RepositionRowOfThings(THING_PHYSDEV, 10.0f); /* don't rearrange them after the initial add. */
|
|
|
|
RepositionRowOfThings(THING_PHYSDEV_CAPTURE, 170.0f); /* don't rearrange them after the initial add. */
|
|
|
|
next_physdev_x = 0.0f;
|
|
|
|
} else {
|
|
|
|
next_physdev_x += physdev_texture->w * 1.5f;
|
|
|
|
}
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return thing;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *CreateTrashcanThing(void)
|
|
|
|
{
|
|
|
|
int winw, winh;
|
|
|
|
SDL_GetWindowSize(state->windows[0], &winw, &winh);
|
|
|
|
return CreateThing(THING_TRASHCAN, winw - trashcan_texture->w, winh - trashcan_texture->h, 10, -1, -1, trashcan_texture, "Drag things here to remove them.");
|
|
|
|
}
|
|
|
|
|
|
|
|
static Thing *CreateDefaultPhysicalDevice(const SDL_bool iscapture)
|
|
|
|
{
|
|
|
|
return CreatePhysicalDeviceThing(iscapture ? SDL_AUDIO_DEVICE_DEFAULT_CAPTURE : SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, iscapture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TickThings(void)
|
|
|
|
{
|
|
|
|
Thing *i;
|
|
|
|
Thing *next;
|
|
|
|
const Uint64 now = SDL_GetTicks();
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
next = i->next; /* in case this deletes itself. */
|
|
|
|
if (i->ontick) {
|
|
|
|
i->ontick(i, now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WindowResized(const int newwinw, const int newwinh)
|
|
|
|
{
|
|
|
|
Thing *i;
|
|
|
|
const float neww = (float) newwinw;
|
|
|
|
const float newh = (float) newwinh;
|
|
|
|
const float oldw = (float) state->window_w;
|
|
|
|
const float oldh = (float) state->window_h;
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = i->next) {
|
2023-08-02 10:13:35 -06:00
|
|
|
const float halfw = i->rect.w / 2.0f;
|
|
|
|
const float halfh = i->rect.h / 2.0f;
|
|
|
|
const float x = (i->rect.x + halfw) / oldw;
|
|
|
|
const float y = (i->rect.y + halfh) / oldh;
|
|
|
|
i->rect.x = (x * neww) - halfw;
|
|
|
|
i->rect.y = (y * newh) - halfh;
|
|
|
|
}
|
|
|
|
state->window_w = newwinw;
|
|
|
|
state->window_h = newwinh;
|
|
|
|
}
|
|
|
|
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
int SDL_AppInit(int argc, char *argv[])
|
2023-08-02 10:13:35 -06:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
2023-11-09 14:29:15 -07:00
|
|
|
if (!state) {
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
return -1;
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
state->window_flags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
|
|
|
|
for (i = 1; i < argc;) {
|
|
|
|
int consumed = SDLTest_CommonArg(state, i);
|
|
|
|
if (consumed == 0) {
|
|
|
|
consumed = -1;
|
|
|
|
/* add our own command lines here. */
|
|
|
|
}
|
|
|
|
if (consumed < 0) {
|
|
|
|
static const char *options[] = {
|
|
|
|
/* add our own command lines here. */
|
|
|
|
/*"[--blend none|blend|add|mod|mul|sub]",*/
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
SDLTest_CommonLogUsage(state, argv[0], options);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
return -1;
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
i += consumed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SDLTest_CommonInit(state)) {
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
return -1;
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
2023-08-08 19:41:37 -06:00
|
|
|
if (state->audio_id) {
|
|
|
|
SDL_CloseAudioDevice(state->audio_id);
|
|
|
|
state->audio_id = 0;
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:13:35 -06:00
|
|
|
SetDefaultTitleBar();
|
|
|
|
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
if ((physdev_texture = CreateTexture("physaudiodev.bmp")) == NULL) { return -1; }
|
|
|
|
if ((logdev_texture = CreateTexture("logaudiodev.bmp")) == NULL) { return -1; }
|
|
|
|
if ((audio_texture = CreateTexture("audiofile.bmp")) == NULL) { return -1; }
|
|
|
|
if ((trashcan_texture = CreateTexture("trashcan.bmp")) == NULL) { return -1; }
|
|
|
|
if ((soundboard_texture = CreateTexture("soundboard.bmp")) == NULL) { return -1; }
|
|
|
|
if ((soundboard_levels_texture = CreateTexture("soundboard_levels.bmp")) == NULL) { return -1; }
|
2023-08-02 10:13:35 -06:00
|
|
|
|
|
|
|
LoadStockWavThings();
|
|
|
|
CreateTrashcanThing();
|
|
|
|
CreateDefaultPhysicalDevice(SDL_FALSE);
|
|
|
|
CreateDefaultPhysicalDevice(SDL_TRUE);
|
|
|
|
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static SDL_bool saw_event = SDL_FALSE;
|
|
|
|
|
|
|
|
int SDL_AppEvent(const SDL_Event *event)
|
|
|
|
{
|
|
|
|
Thing *thing = NULL;
|
|
|
|
|
|
|
|
saw_event = SDL_TRUE;
|
|
|
|
|
|
|
|
switch (event->type) {
|
|
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
|
|
thing = UpdateMouseOver(event->motion.x, event->motion.y);
|
|
|
|
if ((dragging_button == -1) && event->motion.state) {
|
|
|
|
if (event->motion.state & SDL_BUTTON_LMASK) {
|
|
|
|
/* for people that don't have all three buttons... */
|
|
|
|
if (ctrl_held) {
|
|
|
|
dragging_button = SDL_BUTTON_RIGHT;
|
|
|
|
} else if (alt_held) {
|
|
|
|
dragging_button = SDL_BUTTON_MIDDLE;
|
|
|
|
} else {
|
|
|
|
dragging_button = SDL_BUTTON_LEFT;
|
|
|
|
}
|
|
|
|
dragging_button_real = SDL_BUTTON_LEFT;
|
|
|
|
} else if (event->motion.state & SDL_BUTTON_RMASK) {
|
|
|
|
dragging_button = SDL_BUTTON_RIGHT;
|
|
|
|
dragging_button_real = SDL_BUTTON_RIGHT;
|
|
|
|
} else if (event->motion.state & SDL_BUTTON_MMASK) {
|
|
|
|
dragging_button = SDL_BUTTON_MIDDLE;
|
|
|
|
dragging_button_real = SDL_BUTTON_MIDDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dragging_button != -1) {
|
|
|
|
dragging_thing = thing;
|
|
|
|
if (thing && thing->ondrag) {
|
|
|
|
thing->ondrag(thing, dragging_button, event->motion.x, event->motion.y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
droppable_highlighted_thing = NULL;
|
|
|
|
if (dragging_thing) {
|
|
|
|
dragging_thing->rect.x = event->motion.x - (dragging_thing->rect.w / 2);
|
|
|
|
dragging_thing->rect.y = event->motion.y - (dragging_thing->rect.h / 2);
|
|
|
|
if (dragging_thing->can_be_dropped_onto) {
|
|
|
|
thing = FindThingAtPoint(event->motion.x, event->motion.y);
|
|
|
|
if (thing) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; dragging_thing->can_be_dropped_onto[i]; i++) {
|
|
|
|
if (dragging_thing->can_be_dropped_onto[i] == thing->what) {
|
|
|
|
droppable_highlighted_thing = thing;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
|
|
thing = UpdateMouseOver(event->button.x, event->button.y);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
|
|
|
if (dragging_button_real == event->button.button) {
|
|
|
|
Thing *dropped_thing = dragging_thing;
|
|
|
|
dragging_thing = NULL;
|
|
|
|
dragging_button = -1;
|
|
|
|
dragging_button_real = -1;
|
|
|
|
if (dropped_thing && dropped_thing->ondrop) {
|
|
|
|
dropped_thing->ondrop(dropped_thing, event->button.button, event->button.x, event->button.y);
|
|
|
|
}
|
|
|
|
droppable_highlighted_thing = NULL;
|
|
|
|
}
|
|
|
|
thing = UpdateMouseOver(event->button.x, event->button.y);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_MOUSE_WHEEL:
|
|
|
|
UpdateMouseOver(event->wheel.mouseX, event->wheel.mouseY);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_KEY_DOWN:
|
|
|
|
case SDL_EVENT_KEY_UP:
|
2023-11-03 10:27:29 -06:00
|
|
|
ctrl_held = ((event->key.keysym.mod & SDL_KMOD_CTRL) != 0);
|
|
|
|
alt_held = ((event->key.keysym.mod & SDL_KMOD_ALT) != 0);
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_DROP_FILE:
|
2023-11-04 01:55:55 -06:00
|
|
|
SDL_Log("Drop file! '%s'", event->drop.data);
|
|
|
|
LoadWavThing(event->drop.data, event->drop.x, event->drop.y);
|
|
|
|
/* SDL frees event->drop.data for you when you use SDL_AppEvent(). */
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_WINDOW_RESIZED:
|
|
|
|
WindowResized(event->window.data1, event->window.data2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_AUDIO_DEVICE_ADDED:
|
|
|
|
CreatePhysicalDeviceThing(event->adevice.which, event->adevice.iscapture);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_EVENT_AUDIO_DEVICE_REMOVED: {
|
|
|
|
const SDL_AudioDeviceID which = event->adevice.which;
|
|
|
|
Thing *i, *next;
|
|
|
|
SDL_Log("Removing audio device %u", (unsigned int) which);
|
2023-11-09 14:29:15 -07:00
|
|
|
for (i = things; i; i = next) {
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
next = i->next;
|
|
|
|
if (((i->what == THING_PHYSDEV) || (i->what == THING_PHYSDEV_CAPTURE)) && (i->data.physdev.devid == which)) {
|
|
|
|
TrashThing(i);
|
|
|
|
next = things; /* in case we mangled the list. */
|
|
|
|
} else if (((i->what == THING_LOGDEV) || (i->what == THING_LOGDEV_CAPTURE)) && (i->data.logdev.devid == which)) {
|
|
|
|
TrashThing(i);
|
|
|
|
next = things; /* in case we mangled the list. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: break;
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
return SDLTest_CommonEventMainCallbacks(state, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SDL_AppIterate(void)
|
|
|
|
{
|
|
|
|
if (app_ready_ticks == 0) {
|
|
|
|
app_ready_ticks = SDL_GetTicks();
|
|
|
|
}
|
|
|
|
|
|
|
|
TickThings();
|
|
|
|
Draw();
|
|
|
|
|
|
|
|
if (saw_event) {
|
|
|
|
saw_event = SDL_FALSE; /* reset this so we know when SDL_AppEvent() runs again */
|
|
|
|
} else {
|
|
|
|
SDL_Delay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; /* keep going. */
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDL_AppQuit(void)
|
|
|
|
{
|
2023-11-09 14:29:15 -07:00
|
|
|
while (things) {
|
main: Added _optional_ callback entry points.
This lets apps optionally have a handful of callbacks for their entry points instead of a single main function. If used, the actual main/SDL_main/whatever entry point will be implemented in the single-header library SDL_main.h and the app will implement four separate functions:
First:
int SDL_AppInit(int argc, char **argv);
This will be called once before anything else. argc/argv work like they always do. If this returns 0, the app runs. If it returns < 0, the app calls SDL_AppQuit and terminates with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. This function should not go into an infinite mainloop; it should do any one-time startup it requires and then return.
Then:
int SDL_AppIterate(void);
This is called over and over, possibly at the refresh rate of the display or some other metric that the platform dictates. This is where the heart of your app runs. It should return as quickly as reasonably possible, but it's not a "run one memcpy and that's all the time you have" sort of thing. The app should do any game updates, and render a frame of video. If it returns < 0, SDL will call SDL_AppQuit and terminate the process with an exit code that reports an error to the platform. If it returns > 0, the app calls SDL_AppQuit and terminates with an exit code that reports success to the platform. If it returns 0, then SDL_AppIterate will be called again at some regular frequency. The platform may choose to run this more or less (perhaps less in the background, etc), or it might just call this function in a loop as fast as possible. You do not check the event queue in this function (SDL_AppEvent exists for that).
Next:
int SDL_AppEvent(const SDL_Event *event);
This will be called once for each event pushed into the SDL queue. This may be called from any thread, and possibly in parallel to SDL_AppIterate. The fields in event do not need to be free'd (as you would normally need to do for SDL_EVENT_DROP_FILE, etc), and your app should not call SDL_PollEvent, SDL_PumpEvent, etc, as SDL will manage this for you. Return values are the same as from SDL_AppIterate(), so you can terminate in response to SDL_EVENT_QUIT, etc.
Finally:
void SDL_AppQuit(void);
This is called once before terminating the app--assuming the app isn't being forcibly killed or crashed--as a last chance to clean up. After this returns, SDL will call SDL_Quit so the app doesn't have to (but it's safe for the app to call it, too). Process termination proceeds as if the app returned normally from main(), so atexit handles will run, if your platform supports that.
The app does not implement SDL_main if using this. To turn this on, define SDL_MAIN_USE_CALLBACKS before including SDL_main.h. Defines like SDL_MAIN_HANDLED and SDL_MAIN_NOIMPL are also respected for callbacks, if the app wants to do some sort of magic main implementation thing.
In theory, on most platforms these can be implemented in the app itself, but this saves some #ifdefs in the app and lets everyone struggle less against some platforms, and might be more efficient in the long run, too.
On some platforms, it's possible this is the only reasonable way to go, but we haven't actually hit one that 100% requires it yet (but we will, if we want to write a RetroArch backend, for example).
Using the callback entry points works on every platform, because on platforms that don't require them, we can fake them with a simple loop in an internal implementation of the usual SDL_main.
The primary way we expect people to write SDL apps is with SDL_main, and this is not intended to replace it. If the app chooses to use this, it just removes some platform-specific details they might have to otherwise manage, and maybe removes a barrier to entry on some future platform.
Fixes #6785.
Reference PR #8247.
2023-11-01 16:40:41 -06:00
|
|
|
DestroyThing(things); /* make sure all the audio devices are closed, etc. */
|
|
|
|
}
|
|
|
|
|
|
|
|
DestroyTexture(physdev_texture);
|
|
|
|
DestroyTexture(logdev_texture);
|
|
|
|
DestroyTexture(audio_texture);
|
|
|
|
DestroyTexture(trashcan_texture);
|
|
|
|
DestroyTexture(soundboard_texture);
|
|
|
|
DestroyTexture(soundboard_levels_texture);
|
|
|
|
SDLTest_CommonQuit(state);
|
2023-08-02 10:13:35 -06:00
|
|
|
}
|
|
|
|
|