Theme tokens for every editor color with a literal check, System/Dark/Light preference that follows the OS, light goldens, Scene View overlays that keep viewport colors.
591 lines
17 KiB
C
591 lines
17 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "aego_sdl.h"
|
|
|
|
static char aego_title_buf[1024];
|
|
static SDL_Cursor* aego_cursors[AEGO_CURSOR_COUNT];
|
|
static Uint32 aego_dialog_type = 0;
|
|
|
|
typedef struct {
|
|
char mime[128];
|
|
void* data;
|
|
size_t len;
|
|
char* text;
|
|
size_t textlen;
|
|
} aego_clip;
|
|
|
|
static Uint32 aego_mods(SDL_Keymod m) {
|
|
Uint32 r = 0;
|
|
if (m & SDL_KMOD_SHIFT) r |= AEGO_MOD_SHIFT;
|
|
if (m & SDL_KMOD_CTRL) r |= AEGO_MOD_CTRL;
|
|
if (m & SDL_KMOD_ALT) r |= AEGO_MOD_ALT;
|
|
if (m & SDL_KMOD_GUI) r |= AEGO_MOD_SUPER;
|
|
if (m & SDL_KMOD_CAPS) r |= AEGO_MOD_CAPS;
|
|
if (m & SDL_KMOD_NUM) r |= AEGO_MOD_NUM;
|
|
return r;
|
|
}
|
|
|
|
static int aego_button(Uint8 b) {
|
|
switch (b) {
|
|
case SDL_BUTTON_LEFT: return AEGO_BTN_LEFT;
|
|
case SDL_BUTTON_RIGHT: return AEGO_BTN_RIGHT;
|
|
case SDL_BUTTON_MIDDLE: return AEGO_BTN_MIDDLE;
|
|
case SDL_BUTTON_X1: return AEGO_BTN_X1;
|
|
case SDL_BUTTON_X2: return AEGO_BTN_X2;
|
|
}
|
|
return AEGO_BTN_OTHER;
|
|
}
|
|
|
|
bool aego_init(void) {
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) return false;
|
|
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
|
return true;
|
|
}
|
|
|
|
void aego_quit(void) {
|
|
aego_free_cursors();
|
|
SDL_Quit();
|
|
}
|
|
|
|
const char* aego_error(void) {
|
|
return SDL_GetError();
|
|
}
|
|
|
|
const char* aego_video_driver(void) {
|
|
const char* d = SDL_GetCurrentVideoDriver();
|
|
return d ? d : "unknown";
|
|
}
|
|
|
|
Uint64 aego_ticks_ns(void) {
|
|
return SDL_GetTicksNS();
|
|
}
|
|
|
|
int aego_version(void) { return SDL_GetVersion(); }
|
|
int aego_version_major(int v) { return SDL_VERSIONNUM_MAJOR(v); }
|
|
int aego_version_minor(int v) { return SDL_VERSIONNUM_MINOR(v); }
|
|
int aego_version_micro(int v) { return SDL_VERSIONNUM_MICRO(v); }
|
|
|
|
bool aego_gl_setup(int debug, int srgb) {
|
|
bool ok = true;
|
|
int flags = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
|
|
if (debug) flags |= SDL_GL_CONTEXT_DEBUG_FLAG;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, flags) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, srgb ? 1 : 0) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24) && ok;
|
|
ok = SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) && ok;
|
|
return ok;
|
|
}
|
|
|
|
SDL_Window* aego_create_window(const char* title, int w, int h, int resizable, int fullscreen, int highdpi) {
|
|
SDL_WindowFlags flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
|
|
if (resizable) flags |= SDL_WINDOW_RESIZABLE;
|
|
if (fullscreen) flags |= SDL_WINDOW_FULLSCREEN;
|
|
if (highdpi) flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
|
return SDL_CreateWindow(title, w, h, flags);
|
|
}
|
|
|
|
aego_metrics aego_window_metrics(SDL_Window* w) {
|
|
aego_metrics m;
|
|
memset(&m, 0, sizeof(m));
|
|
SDL_GetWindowSize(w, &m.lw, &m.lh);
|
|
SDL_GetWindowSizeInPixels(w, &m.pw, &m.ph);
|
|
m.density = SDL_GetWindowPixelDensity(w);
|
|
m.scale = SDL_GetWindowDisplayScale(w);
|
|
return m;
|
|
}
|
|
|
|
void aego_set_title(SDL_Window* w, const char* s, size_t n) {
|
|
if (n >= sizeof(aego_title_buf)) n = sizeof(aego_title_buf) - 1;
|
|
if (n > 0) memcpy(aego_title_buf, s, n);
|
|
aego_title_buf[n] = 0;
|
|
SDL_SetWindowTitle(w, aego_title_buf);
|
|
}
|
|
|
|
int aego_set_swap_interval(int interval) {
|
|
return SDL_GL_SetSwapInterval(interval) ? 1 : 0;
|
|
}
|
|
|
|
int aego_get_swap_interval(void) {
|
|
int v = 0;
|
|
SDL_GL_GetSwapInterval(&v);
|
|
return v;
|
|
}
|
|
|
|
void* aego_gl_proc(const char* name) {
|
|
return (void*)SDL_GL_GetProcAddress(name);
|
|
}
|
|
|
|
int aego_poll(aego_event* e) {
|
|
SDL_Event ev;
|
|
if (!SDL_PollEvent(&ev)) return 0;
|
|
memset(e, 0, sizeof(*e));
|
|
e->kind = AEGO_EV_NONE;
|
|
switch (ev.type) {
|
|
case SDL_EVENT_QUIT:
|
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
|
e->kind = AEGO_EV_QUIT;
|
|
break;
|
|
case SDL_EVENT_WINDOW_RESIZED:
|
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
|
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
|
|
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
|
|
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
|
|
e->kind = AEGO_EV_RESIZED;
|
|
break;
|
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
e->kind = AEGO_EV_FOCUS_GAINED;
|
|
break;
|
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
e->kind = AEGO_EV_FOCUS_LOST;
|
|
break;
|
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
|
e->kind = AEGO_EV_MINIMIZED;
|
|
break;
|
|
case SDL_EVENT_WINDOW_RESTORED:
|
|
e->kind = AEGO_EV_RESTORED;
|
|
break;
|
|
case SDL_EVENT_KEY_DOWN:
|
|
case SDL_EVENT_KEY_UP:
|
|
e->kind = ev.type == SDL_EVENT_KEY_DOWN ? AEGO_EV_KEY_DOWN : AEGO_EV_KEY_UP;
|
|
e->scancode = (Uint32)ev.key.scancode;
|
|
e->key = (Uint32)ev.key.key;
|
|
e->mod = aego_mods(ev.key.mod);
|
|
e->repeat = ev.key.repeat ? 1 : 0;
|
|
break;
|
|
case SDL_EVENT_TEXT_INPUT:
|
|
e->kind = AEGO_EV_TEXT;
|
|
if (ev.text.text) {
|
|
size_t n = strlen(ev.text.text);
|
|
if (n > AEGO_TEXT_CAPACITY) n = AEGO_TEXT_CAPACITY;
|
|
memcpy(e->text, ev.text.text, n);
|
|
e->textn = (int)n;
|
|
}
|
|
break;
|
|
case SDL_EVENT_TEXT_EDITING:
|
|
e->kind = AEGO_EV_TEXT_EDITING;
|
|
e->data = ev.edit.text;
|
|
e->start = ev.edit.start;
|
|
e->length = ev.edit.length;
|
|
break;
|
|
case SDL_EVENT_DROP_BEGIN:
|
|
case SDL_EVENT_DROP_FILE:
|
|
case SDL_EVENT_DROP_TEXT:
|
|
case SDL_EVENT_DROP_COMPLETE:
|
|
switch (ev.type) {
|
|
case SDL_EVENT_DROP_BEGIN: e->kind = AEGO_EV_DROP_BEGIN; break;
|
|
case SDL_EVENT_DROP_FILE: e->kind = AEGO_EV_DROP_FILE; break;
|
|
case SDL_EVENT_DROP_TEXT: e->kind = AEGO_EV_DROP_TEXT; break;
|
|
default: e->kind = AEGO_EV_DROP_COMPLETE; break;
|
|
}
|
|
e->x = ev.drop.x;
|
|
e->y = ev.drop.y;
|
|
e->data = ev.drop.data;
|
|
break;
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
e->kind = AEGO_EV_MOUSE_MOVE;
|
|
e->x = ev.motion.x;
|
|
e->y = ev.motion.y;
|
|
e->dx = ev.motion.xrel;
|
|
e->dy = ev.motion.yrel;
|
|
break;
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
|
e->kind = ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN ? AEGO_EV_MOUSE_DOWN : AEGO_EV_MOUSE_UP;
|
|
e->x = ev.button.x;
|
|
e->y = ev.button.y;
|
|
e->button = aego_button(ev.button.button);
|
|
break;
|
|
case SDL_EVENT_MOUSE_WHEEL:
|
|
e->kind = AEGO_EV_WHEEL;
|
|
e->wx = ev.wheel.x;
|
|
e->wy = ev.wheel.y;
|
|
e->flipped = ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? 1 : 0;
|
|
e->x = ev.wheel.mouse_x;
|
|
e->y = ev.wheel.mouse_y;
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_ADDED: {
|
|
SDL_Gamepad* g = SDL_OpenGamepad(ev.gdevice.which);
|
|
if (!g) break;
|
|
e->kind = AEGO_EV_PAD_ADDED;
|
|
e->pad = (int)ev.gdevice.which;
|
|
const char* n = SDL_GetGamepadName(g);
|
|
if (n) {
|
|
size_t k = strlen(n);
|
|
if (k > AEGO_TEXT_CAPACITY) k = AEGO_TEXT_CAPACITY;
|
|
memcpy(e->text, n, k);
|
|
e->textn = (int)k;
|
|
}
|
|
break;
|
|
}
|
|
case SDL_EVENT_GAMEPAD_REMOVED: {
|
|
SDL_Gamepad* g = SDL_GetGamepadFromID(ev.gdevice.which);
|
|
if (g) SDL_CloseGamepad(g);
|
|
e->kind = AEGO_EV_PAD_REMOVED;
|
|
e->pad = (int)ev.gdevice.which;
|
|
break;
|
|
}
|
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
|
if (ev.gbutton.button >= AEGO_PAD_BUTTONS) break;
|
|
e->kind = ev.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ? AEGO_EV_PAD_DOWN : AEGO_EV_PAD_UP;
|
|
e->pad = (int)ev.gbutton.which;
|
|
e->button = ev.gbutton.button;
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
|
if (ev.gaxis.axis >= AEGO_PAD_AXES) break;
|
|
e->kind = AEGO_EV_PAD_AXIS;
|
|
e->pad = (int)ev.gaxis.which;
|
|
e->button = ev.gaxis.axis;
|
|
e->wx = ev.gaxis.value < 0 ? ev.gaxis.value / 32768.0f : ev.gaxis.value / 32767.0f;
|
|
break;
|
|
case SDL_EVENT_AUDIO_DEVICE_ADDED:
|
|
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
|
|
case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED:
|
|
if (ev.adevice.recording) break;
|
|
if (ev.type == SDL_EVENT_AUDIO_DEVICE_ADDED) e->kind = AEGO_EV_AUDIO_ADDED;
|
|
else if (ev.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) e->kind = AEGO_EV_AUDIO_REMOVED;
|
|
else e->kind = AEGO_EV_AUDIO_FORMAT;
|
|
e->device = ev.adevice.which;
|
|
break;
|
|
case SDL_EVENT_SYSTEM_THEME_CHANGED:
|
|
e->kind = AEGO_EV_SYSTEM_THEME;
|
|
break;
|
|
default:
|
|
if (aego_dialog_type != 0 && ev.type == aego_dialog_type) {
|
|
e->kind = AEGO_EV_DIALOG;
|
|
e->start = ev.user.code;
|
|
e->length = (int)(intptr_t)ev.user.data2;
|
|
e->data = (const char*)ev.user.data1;
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void aego_text_input_area(SDL_Window* w, int x, int y, int ww, int hh, int cursor) {
|
|
SDL_Rect r;
|
|
r.x = x;
|
|
r.y = y;
|
|
r.w = ww;
|
|
r.h = hh;
|
|
SDL_SetTextInputArea(w, &r, cursor);
|
|
}
|
|
|
|
static const void* aego_clip_data(void* userdata, const char* mime, size_t* size) {
|
|
aego_clip* c = (aego_clip*)userdata;
|
|
if (mime && c->mime[0] && strcmp(mime, c->mime) == 0) {
|
|
*size = c->len;
|
|
return c->data;
|
|
}
|
|
if (mime && (strcmp(mime, "text/plain;charset=utf-8") == 0 || strcmp(mime, "text/plain") == 0)) {
|
|
*size = c->textlen;
|
|
return c->text;
|
|
}
|
|
*size = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static void aego_clip_cleanup(void* userdata) {
|
|
aego_clip* c = (aego_clip*)userdata;
|
|
free(c->data);
|
|
free(c->text);
|
|
free(c);
|
|
}
|
|
|
|
bool aego_set_clipboard(const char* mime, const void* data, size_t len, const char* text, size_t textlen) {
|
|
aego_clip* c = (aego_clip*)calloc(1, sizeof(aego_clip));
|
|
if (!c) return false;
|
|
if (mime) {
|
|
strncpy(c->mime, mime, sizeof(c->mime) - 1);
|
|
}
|
|
if (len > 0) {
|
|
c->data = malloc(len);
|
|
if (!c->data) { aego_clip_cleanup(c); return false; }
|
|
memcpy(c->data, data, len);
|
|
c->len = len;
|
|
}
|
|
c->text = (char*)malloc(textlen + 1);
|
|
if (!c->text) { aego_clip_cleanup(c); return false; }
|
|
if (textlen > 0) memcpy(c->text, text, textlen);
|
|
c->text[textlen] = 0;
|
|
c->textlen = textlen;
|
|
const char* types[3] = { c->mime, "text/plain;charset=utf-8", "text/plain" };
|
|
if (!SDL_SetClipboardData(aego_clip_data, aego_clip_cleanup, c, types, 3)) {
|
|
aego_clip_cleanup(c);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void* aego_get_clipboard(const char* mime, size_t* len) {
|
|
*len = 0;
|
|
if (!SDL_HasClipboardData(mime)) return NULL;
|
|
return SDL_GetClipboardData(mime, len);
|
|
}
|
|
|
|
void aego_set_cursor(int shape) {
|
|
SDL_SystemCursor id = SDL_SYSTEM_CURSOR_DEFAULT;
|
|
switch (shape) {
|
|
case 1: id = SDL_SYSTEM_CURSOR_TEXT; break;
|
|
case 2: id = SDL_SYSTEM_CURSOR_POINTER; break;
|
|
case 3: id = SDL_SYSTEM_CURSOR_MOVE; break;
|
|
case 4: id = SDL_SYSTEM_CURSOR_EW_RESIZE; break;
|
|
case 5: id = SDL_SYSTEM_CURSOR_NS_RESIZE; break;
|
|
case 6: id = SDL_SYSTEM_CURSOR_NWSE_RESIZE; break;
|
|
case 7: id = SDL_SYSTEM_CURSOR_NESW_RESIZE; break;
|
|
case 8: id = SDL_SYSTEM_CURSOR_NOT_ALLOWED; break;
|
|
case 9: id = SDL_SYSTEM_CURSOR_CROSSHAIR; break;
|
|
default: shape = 0; break;
|
|
}
|
|
if (!aego_cursors[shape]) aego_cursors[shape] = SDL_CreateSystemCursor(id);
|
|
if (aego_cursors[shape]) SDL_SetCursor(aego_cursors[shape]);
|
|
}
|
|
|
|
void aego_free_cursors(void) {
|
|
for (int i = 0; i < AEGO_CURSOR_COUNT; i++) {
|
|
if (aego_cursors[i]) {
|
|
SDL_DestroyCursor(aego_cursors[i]);
|
|
aego_cursors[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int aego_displays(SDL_DisplayID* out, int max) {
|
|
int n = 0;
|
|
SDL_DisplayID* ids = SDL_GetDisplays(&n);
|
|
if (!ids) return 0;
|
|
if (n > max) n = max;
|
|
for (int i = 0; i < n; i++) out[i] = ids[i];
|
|
SDL_free(ids);
|
|
return n;
|
|
}
|
|
|
|
bool aego_display_current(SDL_DisplayID id, aego_mode* out) {
|
|
const SDL_DisplayMode* m = SDL_GetCurrentDisplayMode(id);
|
|
if (!m) return false;
|
|
out->w = m->w;
|
|
out->h = m->h;
|
|
out->hz = m->refresh_rate;
|
|
return true;
|
|
}
|
|
|
|
int aego_display_modes(SDL_DisplayID id, aego_mode* out, int max) {
|
|
int n = 0;
|
|
SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(id, &n);
|
|
if (!modes) return 0;
|
|
if (n > max) n = max;
|
|
for (int i = 0; i < n; i++) {
|
|
out[i].w = modes[i]->w;
|
|
out[i].h = modes[i]->h;
|
|
out[i].hz = modes[i]->refresh_rate;
|
|
}
|
|
SDL_free(modes);
|
|
return n;
|
|
}
|
|
|
|
bool aego_set_window_mode(SDL_Window* w, int mode, SDL_DisplayID id, int width, int height, float hz) {
|
|
SDL_Rect b;
|
|
if (!SDL_GetDisplayBounds(id, &b)) return false;
|
|
switch (mode) {
|
|
case 2: {
|
|
SDL_DisplayMode m;
|
|
if (!SDL_GetClosestFullscreenDisplayMode(id, width, height, hz, true, &m)) return false;
|
|
SDL_SetWindowPosition(w, SDL_WINDOWPOS_CENTERED_DISPLAY(id), SDL_WINDOWPOS_CENTERED_DISPLAY(id));
|
|
if (!SDL_SetWindowFullscreenMode(w, &m)) return false;
|
|
if (!SDL_SetWindowFullscreen(w, true)) return false;
|
|
break;
|
|
}
|
|
case 1:
|
|
SDL_SetWindowPosition(w, b.x, b.y);
|
|
if (!SDL_SetWindowFullscreenMode(w, NULL)) return false;
|
|
if (!SDL_SetWindowFullscreen(w, true)) return false;
|
|
break;
|
|
default:
|
|
if (!SDL_SetWindowFullscreen(w, false)) return false;
|
|
SDL_SetWindowBordered(w, true);
|
|
if (width > 0 && height > 0) SDL_SetWindowSize(w, width, height);
|
|
SDL_SetWindowPosition(w, SDL_WINDOWPOS_CENTERED_DISPLAY(id), SDL_WINDOWPOS_CENTERED_DISPLAY(id));
|
|
break;
|
|
}
|
|
SDL_SyncWindow(w);
|
|
return true;
|
|
}
|
|
|
|
SDL_AudioStream* aego_audio_open(int freq, int ch, int frames, int* got_freq, int* got_frames) {
|
|
if (!SDL_WasInit(SDL_INIT_AUDIO) && !SDL_InitSubSystem(SDL_INIT_AUDIO)) return NULL;
|
|
SDL_AudioSpec dev;
|
|
int dev_frames = 0;
|
|
if (freq <= 0) {
|
|
freq = SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &dev, &dev_frames) ? dev.freq : 48000;
|
|
}
|
|
if (frames > 0) {
|
|
char buf[16];
|
|
SDL_snprintf(buf, sizeof(buf), "%d", frames);
|
|
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, buf);
|
|
}
|
|
SDL_AudioSpec spec;
|
|
spec.format = SDL_AUDIO_F32;
|
|
spec.channels = ch;
|
|
spec.freq = freq;
|
|
SDL_AudioStream* s = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
|
|
if (!s) return NULL;
|
|
*got_freq = freq;
|
|
*got_frames = frames;
|
|
if (SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(s), &dev, &dev_frames) && dev_frames > 0) *got_frames = dev_frames;
|
|
SDL_ResumeAudioStreamDevice(s);
|
|
return s;
|
|
}
|
|
|
|
bool aego_audio_put(SDL_AudioStream* s, const float* data, int bytes) {
|
|
return SDL_PutAudioStreamData(s, data, bytes);
|
|
}
|
|
|
|
int aego_audio_queued(SDL_AudioStream* s) {
|
|
int n = SDL_GetAudioStreamQueued(s);
|
|
return n < 0 ? 0 : n;
|
|
}
|
|
|
|
void aego_audio_pause(SDL_AudioStream* s, int on) {
|
|
if (on) SDL_PauseAudioStreamDevice(s);
|
|
else SDL_ResumeAudioStreamDevice(s);
|
|
}
|
|
|
|
void aego_audio_close(SDL_AudioStream* s) {
|
|
SDL_DestroyAudioStream(s);
|
|
}
|
|
|
|
Uint32 aego_audio_device(SDL_AudioStream* s) {
|
|
return SDL_GetAudioStreamDevice(s);
|
|
}
|
|
|
|
typedef struct {
|
|
int id;
|
|
char* start;
|
|
char* name;
|
|
char* pattern;
|
|
SDL_DialogFileFilter filter;
|
|
} aego_dialog_req;
|
|
|
|
static char* aego_dup(const char* s) {
|
|
return s && *s ? SDL_strdup(s) : NULL;
|
|
}
|
|
|
|
static void SDLCALL aego_dialog_done(void* userdata, const char* const* list, int filter) {
|
|
aego_dialog_req* r = (aego_dialog_req*)userdata;
|
|
SDL_Event ev;
|
|
SDL_zero(ev);
|
|
ev.type = aego_dialog_type;
|
|
ev.user.code = r->id;
|
|
if (!list) {
|
|
ev.user.data2 = (void*)(intptr_t)-1;
|
|
} else if (!list[0]) {
|
|
ev.user.data2 = (void*)(intptr_t)0;
|
|
} else {
|
|
ev.user.data1 = SDL_strdup(list[0]);
|
|
ev.user.data2 = (void*)(intptr_t)1;
|
|
}
|
|
(void)filter;
|
|
SDL_free(r->start);
|
|
SDL_free(r->name);
|
|
SDL_free(r->pattern);
|
|
SDL_free(r);
|
|
SDL_PushEvent(&ev);
|
|
}
|
|
|
|
bool aego_dialog(int id, int kind, SDL_Window* w, const char* start, const char* name, const char* pattern) {
|
|
if (aego_dialog_type == 0) {
|
|
aego_dialog_type = SDL_RegisterEvents(1);
|
|
if (aego_dialog_type == 0) return false;
|
|
}
|
|
aego_dialog_req* r = (aego_dialog_req*)SDL_calloc(1, sizeof(*r));
|
|
if (!r) return false;
|
|
r->id = id;
|
|
r->start = aego_dup(start);
|
|
r->name = aego_dup(name);
|
|
r->pattern = aego_dup(pattern);
|
|
r->filter.name = r->name ? r->name : "Files";
|
|
r->filter.pattern = r->pattern;
|
|
int n = r->pattern ? 1 : 0;
|
|
switch (kind) {
|
|
case 0:
|
|
SDL_ShowOpenFolderDialog(aego_dialog_done, r, w, r->start, false);
|
|
break;
|
|
case 1:
|
|
SDL_ShowOpenFileDialog(aego_dialog_done, r, w, n ? &r->filter : NULL, n, r->start, false);
|
|
break;
|
|
default:
|
|
SDL_ShowSaveFileDialog(aego_dialog_done, r, w, n ? &r->filter : NULL, n, r->start);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool aego_alert(const char* title, const char* msg) {
|
|
return SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, msg, NULL);
|
|
}
|
|
|
|
SDL_Surface* aego_icon_surface(int w, int h, const void* rgba) {
|
|
SDL_Surface* s = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_RGBA32);
|
|
if (!s) {
|
|
return NULL;
|
|
}
|
|
for (int y = 0; y < h; y++) {
|
|
SDL_memcpy((Uint8*)s->pixels + y * s->pitch, (const Uint8*)rgba + (size_t)y * w * 4, (size_t)w * 4);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
bool aego_icon_alternate(SDL_Surface* base, SDL_Surface* alt) {
|
|
bool ok = SDL_AddSurfaceAlternateImage(base, alt);
|
|
SDL_DestroySurface(alt);
|
|
return ok;
|
|
}
|
|
|
|
bool aego_set_icon(SDL_Window* w, SDL_Surface* s) {
|
|
bool ok = SDL_SetWindowIcon(w, s);
|
|
SDL_DestroySurface(s);
|
|
return ok;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
#define AEGO_GLAPI __stdcall
|
|
#else
|
|
#define AEGO_GLAPI
|
|
#endif
|
|
|
|
typedef const unsigned char*(AEGO_GLAPI* aego_getstring)(unsigned int);
|
|
|
|
const char* aego_gl_string(unsigned int name) {
|
|
aego_getstring fn = (aego_getstring)SDL_GL_GetProcAddress("glGetString");
|
|
if (!fn) {
|
|
return "";
|
|
}
|
|
const unsigned char* v = fn(name);
|
|
return v ? (const char*)v : "";
|
|
}
|
|
|
|
const char* aego_gl_probe(SDL_Window* w) {
|
|
static char buf[256];
|
|
buf[0] = 0;
|
|
SDL_GL_ResetAttributes();
|
|
SDL_GLContext c = SDL_GL_CreateContext(w);
|
|
if (!c) {
|
|
return buf;
|
|
}
|
|
if (SDL_GL_MakeCurrent(w, c)) {
|
|
SDL_snprintf(buf, sizeof buf, "%s (%s)", aego_gl_string(0x1F02), aego_gl_string(0x1F01));
|
|
}
|
|
SDL_GL_MakeCurrent(w, NULL);
|
|
SDL_GL_DestroyContext(c);
|
|
return buf;
|
|
}
|