|
|
3f9996 |
|
|
|
a20939 |
#include <time.h></time.h>
|
|
|
a20939 |
|
|
|
f63775 |
#include <sdl.h></sdl.h>
|
|
|
3f9996 |
|
|
|
3f9996 |
#include "private.h"
|
|
|
3e7c5f |
#include "drawing.h"
|
|
|
d7d433 |
#include "window.h"
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
a20939 |
static int sdlInitialized = 0;
|
|
|
87fe10 |
static SDL_Window *window;
|
|
|
87fe10 |
static SDL_GLContext context;
|
|
|
1015c5 |
|
|
|
3f9996 |
static int started;
|
|
|
3f9996 |
static int stopped;
|
|
|
8bc1f1 |
static int firstFrame = TRUE;
|
|
|
3f9996 |
static unsigned long long frameCount;
|
|
|
a20939 |
static unsigned long long elapsedTimeUs;
|
|
|
a20939 |
static unsigned long long elapsedTimeSinceLastFrameUs;
|
|
|
a20939 |
static unsigned int prevFrameTimeMs;
|
|
|
3f9996 |
|
|
|
df4105 |
static unsigned long long monotonicMs;
|
|
|
df4105 |
static unsigned int prevMonotonicMs;
|
|
|
df4105 |
|
|
|
534343 |
HeliDialog dialog;
|
|
|
534343 |
|
|
|
3f9996 |
static Callback initCallback;
|
|
|
3f9996 |
static Callback drawCallback;
|
|
|
1015c5 |
static Callback deinitCallback;
|
|
|
3f9996 |
|
|
|
534343 |
static const int minWidth = 200;
|
|
|
534343 |
static const int minHeight = 200;
|
|
|
534343 |
|
|
|
ba9f06 |
static int width = 512;
|
|
|
ba9f06 |
static int height = 512;
|
|
|
8eb855 |
static int resizable;
|
|
|
8eb855 |
static char title[1000];
|
|
|
8eb855 |
static int titleSize = (int)(sizeof(title)/sizeof(*title));
|
|
|
dba3fc |
static double minFPS = HELI_DEFAULT_FPS;
|
|
|
dba3fc |
static double maxFPS = HELI_DEFAULT_FPS;
|
|
|
dba3fc |
static double frameTime = 1.0/HELI_DEFAULT_FPS;
|
|
|
3f9996 |
|
|
|
1015c5 |
static HeliArray keyEvents[6];
|
|
|
1015c5 |
static int keyEventsCount = (int)(sizeof(keyEvents)/sizeof(*keyEvents));
|
|
|
3f9996 |
|
|
|
3f9996 |
static int mouseMovedInFrame;
|
|
|
3f9996 |
static double _mouseX;
|
|
|
3f9996 |
static double _mouseY;
|
|
|
3f9996 |
|
|
|
aaf750 |
static int _mouseScrolledX;
|
|
|
aaf750 |
static int _mouseScrolledY;
|
|
|
aaf750 |
|
|
|
3f9996 |
|
|
|
519bc5 |
static const char* keyAliases[][2] = {
|
|
|
519bc5 |
// as user asked | as internally stored //
|
|
|
519bc5 |
{ "enter" , "return" },
|
|
|
519bc5 |
{ "any enter" , "any return" },
|
|
|
519bc5 |
{ "keypad return" , "keypad enter" } };
|
|
|
519bc5 |
static int keyAliasesCount = (int)(sizeof(keyAliases)/sizeof(*keyAliases));
|
|
|
519bc5 |
static const char* keyGroups[][2] = {
|
|
|
519bc5 |
// as SDL passed | as internally stored //
|
|
|
519bc5 |
{ "left shift" , "any shift" },
|
|
|
519bc5 |
{ "left ctrl" , "any ctrl" },
|
|
|
519bc5 |
{ "left alt" , "any alt" },
|
|
|
519bc5 |
{ "left gui" , "any gui" },
|
|
|
519bc5 |
{ "right shift" , "any shift" },
|
|
|
519bc5 |
{ "right ctrl" , "any ctrl" },
|
|
|
519bc5 |
{ "right alt" , "any alt" },
|
|
|
519bc5 |
{ "right gui" , "any gui" },
|
|
|
519bc5 |
{ "0" , "any 0" },
|
|
|
519bc5 |
{ "1" , "any 1" },
|
|
|
519bc5 |
{ "2" , "any 2" },
|
|
|
519bc5 |
{ "3" , "any 3" },
|
|
|
519bc5 |
{ "4" , "any 4" },
|
|
|
519bc5 |
{ "5" , "any 5" },
|
|
|
519bc5 |
{ "6" , "any 6" },
|
|
|
519bc5 |
{ "7" , "any 7" },
|
|
|
519bc5 |
{ "8" , "any 8" },
|
|
|
519bc5 |
{ "9" , "any 9" },
|
|
|
519bc5 |
{ "/" , "any /" },
|
|
|
519bc5 |
{ "*" , "any *" },
|
|
|
519bc5 |
{ "-" , "any -" },
|
|
|
519bc5 |
{ "+" , "any +" },
|
|
|
519bc5 |
{ "return" , "any return" },
|
|
|
519bc5 |
{ "keypad 0" , "any 0" },
|
|
|
519bc5 |
{ "keypad 1" , "any 1" },
|
|
|
519bc5 |
{ "keypad 2" , "any 2" },
|
|
|
519bc5 |
{ "keypad 3" , "any 3" },
|
|
|
519bc5 |
{ "keypad 4" , "any 4" },
|
|
|
519bc5 |
{ "keypad 5" , "any 5" },
|
|
|
519bc5 |
{ "keypad 6" , "any 6" },
|
|
|
519bc5 |
{ "keypad 7" , "any 7" },
|
|
|
519bc5 |
{ "keypad 8" , "any 8" },
|
|
|
519bc5 |
{ "keypad 9" , "any 9" },
|
|
|
519bc5 |
{ "keypad 9" , "any 9" },
|
|
|
519bc5 |
{ "keypad /" , "any /" },
|
|
|
519bc5 |
{ "keypad *" , "any *" },
|
|
|
519bc5 |
{ "keypad -" , "any -" },
|
|
|
519bc5 |
{ "keypad +" , "any +" },
|
|
|
519bc5 |
{ "keypad enter" , "any return" } };
|
|
|
519bc5 |
static int keyGroupsCount = (int)(sizeof(keyGroups)/sizeof(*keyGroups));
|
|
|
519bc5 |
|
|
|
519bc5 |
|
|
|
3f9996 |
|
|
|
1015c5 |
int keyEventGetCount(KeyEvent mode)
|
|
|
1015c5 |
{ return (int)mode >= 0 && (int)mode <= keyEventsCount ? keyEvents[mode].count : 0; }
|
|
|
1015c5 |
const char *keyEventGet(KeyEvent mode, int i)
|
|
|
1015c5 |
{ return (int)mode >= 0 && (int)mode <= keyEventsCount ? heliArrayGetValue(&keyEvents[mode], i) : NULL; }
|
|
|
1015c5 |
|
|
|
1015c5 |
static int keyEventCheck(KeyEvent mode, const char *code) {
|
|
|
1015c5 |
int count = keyEventGetCount(mode);
|
|
|
1015c5 |
for(int i = 0; i < count; ++i)
|
|
|
bcaca0 |
if (heliStringCompareCi(keyEventGet(mode, i), code) == 0)
|
|
|
1015c5 |
return TRUE;
|
|
|
1015c5 |
return FALSE;
|
|
|
1015c5 |
}
|
|
|
1015c5 |
|
|
|
519bc5 |
static int keyEventAliasesCheck(KeyEvent mode, const char *code) {
|
|
|
519bc5 |
if (keyEventCheck(mode, code)) return TRUE;
|
|
|
519bc5 |
for(int i = 0; i < keyAliasesCount; ++i)
|
|
|
bcaca0 |
if (heliStringCompareCi(code, keyAliases[i][0]) == 0)
|
|
|
519bc5 |
if (keyEventCheck(mode, keyAliases[i][1])) return TRUE;
|
|
|
519bc5 |
return FALSE;
|
|
|
519bc5 |
}
|
|
|
519bc5 |
|
|
|
519bc5 |
static int keyEventAdd(KeyEvent mode, const char *code) {
|
|
|
519bc5 |
if ((int)mode >= 0 && mode <= (int)keyEventsCount && !keyEventCheck(mode, code)) {
|
|
|
bcaca0 |
heliArrayInsert(&keyEvents[mode], -1, heliStringCopyLower(code), &free);
|
|
|
519bc5 |
return TRUE;
|
|
|
519bc5 |
}
|
|
|
519bc5 |
return FALSE;
|
|
|
1015c5 |
}
|
|
|
1015c5 |
|
|
|
519bc5 |
static int keyEventRemove(KeyEvent mode, const char *code) {
|
|
|
519bc5 |
int removed = FALSE;
|
|
|
1015c5 |
if ((int)mode >= 0 && mode <= (int)keyEventsCount)
|
|
|
1015c5 |
for(int i = keyEvents[mode].count-1; i >= 0; --i)
|
|
|
bcaca0 |
if (heliStringCompareCi(keyEvents[mode].items[i].value, code) == 0)
|
|
|
519bc5 |
{ heliArrayRemove(&keyEvents[mode], i); removed = TRUE; }
|
|
|
519bc5 |
return removed;
|
|
|
1015c5 |
}
|
|
|
3f9996 |
|
|
|
519bc5 |
static void pressKey(int mouse, const char *code) {
|
|
|
519bc5 |
keyEventAdd(mouse ? KEYEVENT_MOUSE_DOWN : KEYEVENT_KEY_DOWN, code);
|
|
|
519bc5 |
keyEventAdd(mouse ? KEYEVENT_MOUSE_WENTDOWN : KEYEVENT_KEY_WENTDOWN, code);
|
|
|
519bc5 |
if (!mouse) {
|
|
|
519bc5 |
for(int i = 0; i < keyGroupsCount; ++i) {
|
|
|
bcaca0 |
if (heliStringCompareCi(code, keyGroups[i][0]) == 0) {
|
|
|
519bc5 |
keyEventAdd(KEYEVENT_KEY_DOWN, keyGroups[i][1]);
|
|
|
519bc5 |
keyEventAdd(KEYEVENT_KEY_WENTDOWN, keyGroups[i][1]);
|
|
|
519bc5 |
}
|
|
|
519bc5 |
}
|
|
|
519bc5 |
}
|
|
|
519bc5 |
}
|
|
|
519bc5 |
|
|
|
519bc5 |
static void releaseKey(int mouse, const char *code) {
|
|
|
519bc5 |
if (!keyEventRemove(mouse ? KEYEVENT_MOUSE_DOWN : KEYEVENT_KEY_DOWN, code))
|
|
|
519bc5 |
return;
|
|
|
519bc5 |
keyEventAdd(mouse ? KEYEVENT_MOUSE_WENTUP : KEYEVENT_KEY_WENTUP, code);
|
|
|
519bc5 |
if (mouse)
|
|
|
519bc5 |
return;
|
|
|
519bc5 |
for(int i = 0; i < keyGroupsCount; ++i) {
|
|
|
bcaca0 |
if (heliStringCompareCi(code, keyGroups[i][0]) != 0) continue;
|
|
|
519bc5 |
int allRemoved = TRUE;
|
|
|
519bc5 |
for(int j = 0; j < keyGroupsCount; ++j)
|
|
|
bcaca0 |
if ( heliStringCompareCi(keyGroups[i][1], keyGroups[j][1]) == 0
|
|
|
bcaca0 |
&& keyEventCheck(KEYEVENT_KEY_DOWN, keyGroups[j][0]))
|
|
|
519bc5 |
{ allRemoved = FALSE; break; }
|
|
|
519bc5 |
if (allRemoved)
|
|
|
519bc5 |
if (keyEventRemove(KEYEVENT_KEY_DOWN, keyGroups[i][1]))
|
|
|
519bc5 |
keyEventAdd(KEYEVENT_KEY_WENTUP, keyGroups[i][1]);
|
|
|
519bc5 |
}
|
|
|
519bc5 |
}
|
|
|
519bc5 |
|
|
|
519bc5 |
static void releaseAllKeys() {
|
|
|
519bc5 |
int count = keyEventGetCount(KEYEVENT_KEY_DOWN);
|
|
|
519bc5 |
for(int i = count-1; i >= 0; --i)
|
|
|
519bc5 |
keyEventAdd(KEYEVENT_KEY_WENTUP, keyEventGet(KEYEVENT_KEY_DOWN, i));
|
|
|
519bc5 |
heliArrayClear(&keyEvents[KEYEVENT_KEY_DOWN]);
|
|
|
1015c5 |
|
|
|
519bc5 |
count = keyEventGetCount(KEYEVENT_MOUSE_DOWN);
|
|
|
519bc5 |
for(int i = count-1; i >= 0; --i)
|
|
|
519bc5 |
keyEventAdd(KEYEVENT_MOUSE_WENTUP, keyEventGet(KEYEVENT_MOUSE_DOWN, i));
|
|
|
519bc5 |
heliArrayClear(&keyEvents[KEYEVENT_MOUSE_DOWN]);
|
|
|
519bc5 |
}
|
|
|
519bc5 |
|
|
|
519bc5 |
|
|
|
519bc5 |
|
|
|
8535a3 |
int keyDown(const char *code)
|
|
|
519bc5 |
{ return keyEventAliasesCheck(KEYEVENT_KEY_DOWN, code); }
|
|
|
07b70f |
int keyWentDown(const char *code)
|
|
|
519bc5 |
{ return keyEventAliasesCheck(KEYEVENT_KEY_WENTDOWN, code); }
|
|
|
07b70f |
int keyWentUp(const char *code)
|
|
|
519bc5 |
{ return keyEventAliasesCheck(KEYEVENT_KEY_WENTUP, code); }
|
|
|
3f9996 |
|
|
|
3f9996 |
int mouseDidMove()
|
|
|
3f9996 |
{ return mouseMovedInFrame; }
|
|
|
8535a3 |
int mouseDown(const char *code)
|
|
|
1015c5 |
{ return keyEventCheck(KEYEVENT_MOUSE_DOWN, code); }
|
|
|
8535a3 |
int mouseWentDown(const char *code)
|
|
|
1015c5 |
{ return keyEventCheck(KEYEVENT_MOUSE_WENTDOWN, code); }
|
|
|
8535a3 |
int mouseWentUp(const char *code)
|
|
|
1015c5 |
{ return keyEventCheck(KEYEVENT_MOUSE_WENTUP, code); }
|
|
|
aaf750 |
int mouseScrolledX()
|
|
|
aaf750 |
{ return _mouseScrolledX; }
|
|
|
aaf750 |
int mouseScrolledY()
|
|
|
aaf750 |
{ return _mouseScrolledY; }
|
|
|
3f9996 |
double mouseX()
|
|
|
3f9996 |
{ return _mouseX; }
|
|
|
3f9996 |
double mouseY()
|
|
|
3f9996 |
{ return _mouseY; }
|
|
|
3f9996 |
|
|
|
d7d433 |
double mouseTransformedX() {
|
|
|
8bc1f1 |
double x = mouseX(), y = mouseY();
|
|
|
8bc1f1 |
heliGLBackTransform(&x, &y);
|
|
|
8bc1f1 |
return x;
|
|
|
8bc1f1 |
}
|
|
|
8bc1f1 |
|
|
|
d7d433 |
double mouseTransformedY() {
|
|
|
8bc1f1 |
double x = mouseX(), y = mouseY();
|
|
|
8bc1f1 |
heliGLBackTransform(&x, &y);
|
|
|
8bc1f1 |
return y;
|
|
|
8bc1f1 |
}
|
|
|
8bc1f1 |
|
|
|
981405 |
static void resize(int w, int h) {
|
|
|
534343 |
w = w > minWidth ? w : minWidth;
|
|
|
534343 |
h = h > minHeight ? h : minHeight;
|
|
|
59dae5 |
if (width != w || height != h) {
|
|
|
a20939 |
width = w;
|
|
|
a20939 |
height = h;
|
|
|
a20939 |
if (started && !stopped && window)
|
|
|
a20939 |
SDL_SetWindowSize(window, width, height);
|
|
|
981405 |
}
|
|
|
981405 |
}
|
|
|
8bc1f1 |
|
|
|
d7d433 |
int windowGetWidth()
|
|
|
3f9996 |
{ return width; }
|
|
|
d7d433 |
void windowSetWidth(int w)
|
|
|
981405 |
{ resize(w, height); }
|
|
|
3f9996 |
|
|
|
d7d433 |
int windowGetHeight()
|
|
|
3f9996 |
{ return height; }
|
|
|
d7d433 |
void windowSetHeight(int h)
|
|
|
981405 |
{ resize(width, h); }
|
|
|
3f9996 |
|
|
|
d7d433 |
void windowSetSize(int w, int h)
|
|
|
ca6bde |
{ resize(w, h); }
|
|
|
ca6bde |
|
|
|
d7d433 |
int windowGetResizable()
|
|
|
8eb855 |
{ return resizable; }
|
|
|
d7d433 |
void windowSetResizable(int r) {
|
|
|
8eb855 |
if (resizable == r) return;
|
|
|
8eb855 |
resizable = r ? TRUE : FALSE;
|
|
|
8eb855 |
if (started && !stopped && window)
|
|
|
a20939 |
SDL_SetWindowResizable(window, resizable);
|
|
|
8eb855 |
}
|
|
|
8eb855 |
|
|
|
d7d433 |
const char* windowGetTitle()
|
|
|
8eb855 |
{ return title; }
|
|
|
d7d433 |
void windowSetTitle(const char *t) {
|
|
|
8eb855 |
int changed = FALSE;
|
|
|
8eb855 |
for(int i = 0; i < titleSize-1; ++i) {
|
|
|
8eb855 |
if (title[i] != t[i]) changed = TRUE;
|
|
|
8eb855 |
title[i] = t[i];
|
|
|
8eb855 |
if (!t[i]) break;
|
|
|
8eb855 |
}
|
|
|
8eb855 |
if (changed && started && !stopped && window)
|
|
|
a20939 |
SDL_SetWindowTitle(window, title);
|
|
|
8eb855 |
}
|
|
|
8eb855 |
|
|
|
d7d433 |
double windowGetMinFrameRate()
|
|
|
8bc1f1 |
{ return minFPS; }
|
|
|
d7d433 |
double windowGetMaxFrameRate()
|
|
|
8bc1f1 |
{ return minFPS; }
|
|
|
d7d433 |
void windowSetFrameRateEx(double minFrameRate, double maxFrameRate) {
|
|
|
dba3fc |
if (!(minFrameRate > HELI_MIN_FPS)) minFrameRate = HELI_MIN_FPS;
|
|
|
dba3fc |
if (!(minFrameRate < HELI_MAX_FPS)) minFrameRate = HELI_MAX_FPS;
|
|
|
dba3fc |
if (!(maxFrameRate > HELI_MIN_FPS)) maxFrameRate = HELI_MIN_FPS;
|
|
|
dba3fc |
if (!(maxFrameRate < HELI_MAX_FPS)) maxFrameRate = HELI_MAX_FPS;
|
|
|
8bc1f1 |
if (minFrameRate > maxFrameRate) minFrameRate = maxFrameRate;
|
|
|
8bc1f1 |
minFPS = minFrameRate;
|
|
|
8bc1f1 |
maxFPS = maxFrameRate;
|
|
|
3f9996 |
}
|
|
|
d7d433 |
void windowSetFrameRate(double frameRate)
|
|
|
d7d433 |
{ windowSetFrameRateEx(frameRate, frameRate); }
|
|
|
d7d433 |
void windowSetVariableFrameRate()
|
|
|
d7d433 |
{ windowSetFrameRateEx(HELI_MIN_FPS, HELI_MAX_FPS); }
|
|
|
8bc1f1 |
|
|
|
d7d433 |
double windowGetFrameTime()
|
|
|
8bc1f1 |
{ return frameTime; }
|
|
|
8bc1f1 |
|
|
|
d7d433 |
int windowGetFrameCount()
|
|
|
3f9996 |
{ return (int)frameCount; }
|
|
|
d7d433 |
double windowGetSeconds()
|
|
|
a20939 |
{ return started ? elapsedTimeUs*1e-6 : 0.0; }
|
|
|
3f9996 |
|
|
|
df4105 |
unsigned long long windowGetMonotonicMilliseconds() {
|
|
|
df4105 |
unsigned int ms = SDL_GetTicks();
|
|
|
df4105 |
monotonicMs += ms - prevMonotonicMs;
|
|
|
df4105 |
prevMonotonicMs = ms;
|
|
|
df4105 |
return monotonicMs;
|
|
|
df4105 |
}
|
|
|
df4105 |
|
|
|
df4105 |
double windowGetMonotonicSeconds()
|
|
|
df4105 |
{ return started ? monotonicMs*1e-3 : 0.0; }
|
|
|
df4105 |
|
|
|
d7d433 |
void windowSetInit(Callback init)
|
|
|
3f9996 |
{ initCallback = init; }
|
|
|
d7d433 |
void windowSetDraw(Callback draw)
|
|
|
3f9996 |
{ drawCallback = draw; }
|
|
|
d7d433 |
void windowSetDeinit(Callback deinit)
|
|
|
1015c5 |
{ deinitCallback = deinit; }
|
|
|
d7d433 |
void windowStop()
|
|
|
3f9996 |
{ if (started) stopped = TRUE; }
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3954ba |
void messageBox(const char *message) {
|
|
|
3954ba |
SDL_ShowSimpleMessageBox(
|
|
|
3954ba |
SDL_MESSAGEBOX_INFORMATION,
|
|
|
3954ba |
title,
|
|
|
3954ba |
message,
|
|
|
3954ba |
window );
|
|
|
534343 |
prevFrameTimeMs = SDL_GetTicks();
|
|
|
534343 |
}
|
|
|
534343 |
|
|
|
f8dca4 |
int questionBox(const char *question, const char *answer0, const char *answer1) {
|
|
|
f8dca4 |
SDL_MessageBoxButtonData buttons[2] = {};
|
|
|
f8dca4 |
|
|
|
f8dca4 |
buttons[0].buttonid = 1;
|
|
|
f8dca4 |
buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
|
|
|
f8dca4 |
buttons[0].text = answer1;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
buttons[1].buttonid = 0;
|
|
|
f8dca4 |
buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
|
|
|
f8dca4 |
buttons[1].text = answer0;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
SDL_MessageBoxData data = {};
|
|
|
f8dca4 |
data.flags = SDL_MESSAGEBOX_INFORMATION;
|
|
|
f8dca4 |
data.window = window;
|
|
|
f8dca4 |
data.title = title;
|
|
|
f8dca4 |
data.message = question;
|
|
|
f8dca4 |
data.buttons = buttons;
|
|
|
f8dca4 |
data.numbuttons = 2;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
int buttonid = 0;;
|
|
|
f8dca4 |
SDL_ShowMessageBox(&data, &buttonid);
|
|
|
f8dca4 |
prevFrameTimeMs = SDL_GetTicks();
|
|
|
f8dca4 |
|
|
|
f8dca4 |
return buttonid;
|
|
|
f8dca4 |
}
|
|
|
f8dca4 |
|
|
|
f8dca4 |
int questionBox3(const char* question, const char* answer0, const char* answer1, const char* answer2) {
|
|
|
f8dca4 |
SDL_MessageBoxButtonData buttons[3] = {};
|
|
|
f8dca4 |
|
|
|
f8dca4 |
buttons[0].buttonid = 2;
|
|
|
f8dca4 |
buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
|
|
|
f8dca4 |
buttons[0].text = answer2;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
buttons[1].buttonid = 1;
|
|
|
f8dca4 |
buttons[1].text = answer1;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
buttons[2].buttonid = 0;
|
|
|
f8dca4 |
buttons[2].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
|
|
|
f8dca4 |
buttons[2].text = answer0;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
SDL_MessageBoxData data = {};
|
|
|
f8dca4 |
data.flags = SDL_MESSAGEBOX_INFORMATION;
|
|
|
f8dca4 |
data.window = window;
|
|
|
f8dca4 |
data.title = title;
|
|
|
f8dca4 |
data.message = question;
|
|
|
f8dca4 |
data.buttons = buttons;
|
|
|
f8dca4 |
data.numbuttons = 3;
|
|
|
f8dca4 |
|
|
|
f8dca4 |
int buttonid = 0;;
|
|
|
f8dca4 |
SDL_ShowMessageBox(&data, &buttonid);
|
|
|
f8dca4 |
prevFrameTimeMs = SDL_GetTicks();
|
|
|
f8dca4 |
|
|
|
f8dca4 |
return buttonid;
|
|
|
f8dca4 |
}
|
|
|
f8dca4 |
|
|
|
dba3fc |
int askText(const char *question, char *answer, int maxAnswerSize)
|
|
|
dba3fc |
{ return askTextEx(question, answer, maxAnswerSize, FALSE, FALSE); }
|
|
|
534343 |
|
|
|
7ebc4f |
int askTextf(const char *question, const char *format, ...) {
|
|
|
7ebc4f |
char buf[2048] = {};
|
|
|
7ebc4f |
if (!askText(question, buf, sizeof(buf)))
|
|
|
7ebc4f |
return EOF;
|
|
|
7ebc4f |
va_list args;
|
|
|
7ebc4f |
va_start(args, format);
|
|
|
7ebc4f |
int result = vsscanf(buf, format, args);
|
|
|
7ebc4f |
va_end(args);
|
|
|
7ebc4f |
return result;
|
|
|
7ebc4f |
}
|
|
|
7ebc4f |
|
|
|
534343 |
|
|
|
534343 |
static void resetEvents() {
|
|
|
534343 |
dialog.newText[0] = 0;
|
|
|
aaf750 |
_mouseScrolledX = _mouseScrolledY = 0;
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_KEY_WENTDOWN]);
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_KEY_WENTUP]);
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_MOUSE_WENTDOWN]);
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_MOUSE_WENTUP]);
|
|
|
3954ba |
}
|
|
|
3954ba |
|
|
|
3954ba |
|
|
|
a20939 |
static void draw() {
|
|
|
5df077 |
// touch monotonic timer to not skip the moment of overflow
|
|
|
5df077 |
windowGetMonotonicMilliseconds();
|
|
|
5df077 |
|
|
|
a20939 |
unsigned int currentFrameTimeMs = SDL_GetTicks();
|
|
|
8bc1f1 |
unsigned long long deltaUs = firstFrame ? 0 : (currentFrameTimeMs - prevFrameTimeMs)*1000ull;
|
|
|
a20939 |
prevFrameTimeMs = currentFrameTimeMs;
|
|
|
d1f083 |
|
|
|
8bc1f1 |
double actualMinFPS = minFPS, actualMaxFPS = maxFPS;
|
|
|
8bc1f1 |
if (dialog.shown) { actualMinFPS = 1, actualMaxFPS = 100; }
|
|
|
8bc1f1 |
|
|
|
8bc1f1 |
unsigned long long minTimeStepUs = (unsigned long long)round(1e6/actualMaxFPS);
|
|
|
8bc1f1 |
unsigned long long maxTimeStepUs = (unsigned long long)round(1e6/actualMinFPS);
|
|
|
8bc1f1 |
elapsedTimeSinceLastFrameUs += deltaUs;
|
|
|
8bc1f1 |
if (elapsedTimeSinceLastFrameUs > 2000000)
|
|
|
8bc1f1 |
elapsedTimeSinceLastFrameUs = 2000000;
|
|
|
8bc1f1 |
if (firstFrame || elapsedTimeSinceLastFrameUs >= minTimeStepUs) {
|
|
|
8bc1f1 |
unsigned long long encountedTimeUs = elapsedTimeSinceLastFrameUs;
|
|
|
8bc1f1 |
if (encountedTimeUs > maxTimeStepUs) encountedTimeUs = maxTimeStepUs;
|
|
|
8bc1f1 |
double dt = encountedTimeUs*1e-6;
|
|
|
8bc1f1 |
|
|
|
8bc1f1 |
if (!firstFrame) elapsedTimeSinceLastFrameUs -= encountedTimeUs;
|
|
|
8bc1f1 |
|
|
|
8bc1f1 |
if (!dialog.shown) {
|
|
|
8bc1f1 |
elapsedTimeUs += encountedTimeUs;
|
|
|
8bc1f1 |
++frameCount;
|
|
|
8bc1f1 |
frameTime = firstFrame ? 1/maxFPS : dt;
|
|
|
dba3fc |
heliAnimationUpdate(dt);
|
|
|
8bc1f1 |
heliSpriteUpdate(dt);
|
|
|
8bc1f1 |
}
|
|
|
09c823 |
heliSoundUpdate();
|
|
|
650f35 |
|
|
|
8bc1f1 |
firstFrame = FALSE;
|
|
|
8bc1f1 |
|
|
|
1d641c |
viewportByWindow();
|
|
|
1d641c |
projectionByViewport();
|
|
|
a20939 |
|
|
|
a20939 |
heliDrawingPrepareFrame();
|
|
|
8bc1f1 |
if (dialog.shown) {
|
|
|
8bc1f1 |
heliDialogDraw(&dialog);
|
|
|
8bc1f1 |
} else {
|
|
|
8bc1f1 |
if (drawCallback)
|
|
|
8bc1f1 |
drawCallback();
|
|
|
8bc1f1 |
}
|
|
|
650f35 |
|
|
|
534343 |
resetEvents();
|
|
|
a20939 |
SDL_GL_SwapWindow(window);
|
|
|
8bc1f1 |
}
|
|
|
8bc1f1 |
|
|
|
8bc1f1 |
unsigned long long addUs = elapsedTimeSinceLastFrameUs + (SDL_GetTicks() - prevFrameTimeMs)*1000ull;
|
|
|
8bc1f1 |
if (addUs < minTimeStepUs) {
|
|
|
8bc1f1 |
unsigned long long waitUs = minTimeStepUs - addUs;
|
|
|
8bc1f1 |
if (waitUs > 2000)
|
|
|
8bc1f1 |
SDL_Delay( (unsigned int)((waitUs + 500)/1000ull) );
|
|
|
650f35 |
}
|
|
|
a20939 |
}
|
|
|
a20939 |
|
|
|
a20939 |
|
|
|
a20939 |
static void deinit() {
|
|
|
df4105 |
monotonicMs = 0;
|
|
|
df4105 |
prevMonotonicMs = 0;
|
|
|
df4105 |
|
|
|
1d641c |
heliGLStencilOpSeparatePtr = NULL;
|
|
|
87fe10 |
|
|
|
a20939 |
if (context) SDL_GL_DeleteContext(context);
|
|
|
a20939 |
context = NULL;
|
|
|
650f35 |
|
|
|
a20939 |
if (window) SDL_DestroyWindow(window);
|
|
|
a20939 |
window = NULL;
|
|
|
650f35 |
|
|
|
a20939 |
if (sdlInitialized) SDL_Quit();
|
|
|
a20939 |
sdlInitialized = FALSE;
|
|
|
a20939 |
}
|
|
|
a20939 |
|
|
|
a20939 |
|
|
|
a20939 |
static int init() {
|
|
|
a20939 |
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
|
|
|
3954ba |
fprintf(stderr, "helianthus: SDL_Init failed\n");
|
|
|
a20939 |
deinit();
|
|
|
a20939 |
return FALSE;
|
|
|
a20939 |
}
|
|
|
a20939 |
sdlInitialized = TRUE;
|
|
|
a20939 |
|
|
|
a20939 |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
a20939 |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
|
b9c036 |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
|
|
ba9f06 |
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
a20939 |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
|
a20939 |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
|
|
|
a20939 |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
a20939 |
SDL_GL_SetSwapInterval(1);
|
|
|
a20939 |
|
|
|
a20939 |
unsigned int flags = SDL_WINDOW_OPENGL;
|
|
|
a20939 |
if (resizable) flags |= SDL_WINDOW_RESIZABLE;
|
|
|
354283 |
|
|
|
a20939 |
window = SDL_CreateWindow(
|
|
|
354283 |
title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
|
354283 |
width, height, flags );
|
|
|
354283 |
|
|
|
354283 |
if (!window) {
|
|
|
354283 |
// try to create window without multisampling
|
|
|
354283 |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
|
|
354283 |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
|
|
354283 |
window = SDL_CreateWindow(
|
|
|
354283 |
title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
|
354283 |
width, height, flags );
|
|
|
354283 |
}
|
|
|
354283 |
|
|
|
a20939 |
|
|
|
a20939 |
if (!window) {
|
|
|
3954ba |
fprintf(stderr, "helianthus: cannot create window: %s\n", SDL_GetError());
|
|
|
a20939 |
SDL_ClearError();
|
|
|
a20939 |
deinit();
|
|
|
a20939 |
return FALSE;
|
|
|
a20939 |
}
|
|
|
a20939 |
|
|
|
534343 |
SDL_SetWindowMinimumSize(window, minWidth, minHeight);
|
|
|
534343 |
|
|
|
a20939 |
context = SDL_GL_CreateContext(window);
|
|
|
a20939 |
if (!context) {
|
|
|
3954ba |
fprintf(stderr, "helianthus: cannot create OpenGL context: %s\n", SDL_GetError());
|
|
|
a20939 |
SDL_ClearError();
|
|
|
a20939 |
deinit();
|
|
|
a20939 |
return FALSE;
|
|
|
e034e4 |
}
|
|
|
650f35 |
|
|
|
1d641c |
heliGLBlendFuncSeparatePtr = SDL_GL_GetProcAddress("glBlendFuncSeparate");
|
|
|
1d641c |
heliGLStencilOpSeparatePtr = SDL_GL_GetProcAddress("glStencilOpSeparate");
|
|
|
1d641c |
heliGLTexImage2DMultisamplePtr = SDL_GL_GetProcAddress("glTexImage2DMultisample");
|
|
|
1d641c |
heliGLGenFramebuffersPtr = SDL_GL_GetProcAddress("glGenFramebuffers");
|
|
|
1d641c |
heliGLDeleteFramebuffersPtr = SDL_GL_GetProcAddress("glDeleteFramebuffers");
|
|
|
1d641c |
heliGLBindFramebufferPtr = SDL_GL_GetProcAddress("glBindFramebuffer");
|
|
|
1d641c |
heliGLBlitFramebufferPtr = SDL_GL_GetProcAddress("glBlitFramebuffer");
|
|
|
1d641c |
heliGLFramebufferRenderbufferPtr = SDL_GL_GetProcAddress("glFramebufferRenderbuffer");
|
|
|
1d641c |
heliGLFramebufferTexture2DPtr = SDL_GL_GetProcAddress("glFramebufferTexture2D");
|
|
|
909bc2 |
heliGLCheckFramebufferStatusPtr = SDL_GL_GetProcAddress("glCheckFramebufferStatus");
|
|
|
1d641c |
heliGLGenRenderbuffersPtr = SDL_GL_GetProcAddress("glGenRenderbuffers");
|
|
|
1d641c |
heliGLDeleteRenderbuffersPtr = SDL_GL_GetProcAddress("glDeleteRenderbuffers");
|
|
|
1d641c |
heliGLBindRenderbufferPtr = SDL_GL_GetProcAddress("glBindRenderbuffer");
|
|
|
1d641c |
heliGLRenderbufferStoragePtr = SDL_GL_GetProcAddress("glRenderbufferStorage");
|
|
|
1d641c |
heliGLRenderbufferStorageMultisamplePtr = SDL_GL_GetProcAddress("glRenderbufferStorageMultisample");
|
|
|
1d641c |
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (int*)&heliGLWindowFramebufferReadId);
|
|
|
1d641c |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&heliGLWindowFramebufferDrawId);
|
|
|
87fe10 |
|
|
|
df4105 |
monotonicMs = 0;
|
|
|
df4105 |
prevMonotonicMs = SDL_GetTicks();
|
|
|
df4105 |
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
a20939 |
|
|
|
a20939 |
static void handleEvent(SDL_Event *e) {
|
|
|
a20939 |
if (e->type == SDL_QUIT) {
|
|
|
a20939 |
stopped = TRUE;
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->type == SDL_WINDOWEVENT) {
|
|
|
a20939 |
if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
|
|
|
a20939 |
stopped = TRUE;
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
|
a20939 |
width = e->window.data1;
|
|
|
a20939 |
height = e->window.data2;
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
|
|
519bc5 |
releaseAllKeys();
|
|
|
1015c5 |
}
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->type == SDL_KEYDOWN || e->type == SDL_KEYUP) {
|
|
|
bcaca0 |
const char *keyname = SDL_GetKeyName(e->key.keysym.sym);
|
|
|
bcaca0 |
if (keyname && *keyname) {
|
|
|
519bc5 |
if (e->type == SDL_KEYDOWN)
|
|
|
519bc5 |
pressKey(FALSE, keyname); else releaseKey(FALSE, keyname);
|
|
|
a20939 |
}
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP) {
|
|
|
a20939 |
char *button = NULL;
|
|
|
a20939 |
switch(e->button.button) {
|
|
|
a20939 |
case SDL_BUTTON_LEFT: button = "left"; break;
|
|
|
a20939 |
case SDL_BUTTON_MIDDLE: button = "middle"; break;
|
|
|
a20939 |
case SDL_BUTTON_RIGHT: button = "right"; break;
|
|
|
a20939 |
default: break;
|
|
|
a20939 |
}
|
|
|
a20939 |
if (button) {
|
|
|
519bc5 |
if (e->type == SDL_MOUSEBUTTONDOWN)
|
|
|
519bc5 |
pressKey(TRUE, button); else releaseKey(TRUE, button);
|
|
|
a20939 |
}
|
|
|
a20939 |
_mouseX = e->button.x;
|
|
|
a20939 |
_mouseY = e->button.y;
|
|
|
a20939 |
} else
|
|
|
a20939 |
if (e->type == SDL_MOUSEMOTION) {
|
|
|
a20939 |
_mouseX = e->motion.x;
|
|
|
a20939 |
_mouseY = e->motion.y;
|
|
|
534343 |
} else
|
|
|
aaf750 |
if (e->type == SDL_MOUSEWHEEL) {
|
|
|
aaf750 |
_mouseScrolledX += e->wheel.x;
|
|
|
aaf750 |
_mouseScrolledY += e->wheel.y;
|
|
|
aaf750 |
} else
|
|
|
534343 |
if (e->type == SDL_TEXTINPUT) {
|
|
|
534343 |
if (dialog.shown) {
|
|
|
534343 |
int len = strlen(dialog.newText);
|
|
|
534343 |
int newlen = strlen(e->text.text);
|
|
|
534343 |
int dl = len + newlen + 1 - sizeof(dialog.newText);
|
|
|
534343 |
if (dl > 0) newlen -= dl;
|
|
|
534343 |
if (newlen > 0) {
|
|
|
534343 |
memcpy(dialog.newText + len, e->text.text, newlen);
|
|
|
534343 |
dialog.newText[len + newlen] = 0;
|
|
|
534343 |
}
|
|
|
534343 |
}
|
|
|
534343 |
}
|
|
|
534343 |
}
|
|
|
534343 |
|
|
|
534343 |
|
|
|
dba3fc |
int askTextEx(const char *question, char *answer, int maxAnswerSize, int multiline, int password) {
|
|
|
534343 |
if (maxAnswerSize < 0 || !answer) maxAnswerSize = 0;
|
|
|
534343 |
|
|
|
534343 |
memset(&dialog, 0, sizeof(dialog));
|
|
|
534343 |
dialog.shown = TRUE;
|
|
|
534343 |
dialog.question = question ? question : "";
|
|
|
534343 |
dialog.answer = calloc(1, maxAnswerSize + 1);
|
|
|
97fe4e |
dialog.passwordText = calloc(1, maxAnswerSize*4 + 1);
|
|
|
534343 |
dialog.maxAnswerSize = maxAnswerSize - 1;
|
|
|
534343 |
if (maxAnswerSize > 0) memcpy(dialog.answer, answer, maxAnswerSize);
|
|
|
97fe4e |
dialog.multiline = multiline != 0 && password == 0;
|
|
|
534343 |
dialog.password = password != 0;
|
|
|
534343 |
dialog.pos = dialog.selPos = strlen(dialog.answer);
|
|
|
534343 |
dialog.success = FALSE;
|
|
|
534343 |
|
|
|
534343 |
SDL_StartTextInput();
|
|
|
534343 |
while(dialog.shown && !stopped) {
|
|
|
534343 |
SDL_Event event;
|
|
|
534343 |
while (SDL_PollEvent(&event))
|
|
|
534343 |
handleEvent(&event);
|
|
|
534343 |
draw();
|
|
|
3f9996 |
}
|
|
|
534343 |
SDL_StopTextInput();
|
|
|
534343 |
|
|
|
dba3fc |
int success = dialog.success;
|
|
|
534343 |
if (dialog.success && maxAnswerSize > 0) strcpy(answer, dialog.answer);
|
|
|
534343 |
free(dialog.answer);
|
|
|
97fe4e |
free(dialog.passwordText);
|
|
|
534343 |
memset(&dialog, 0, sizeof(dialog));
|
|
|
534343 |
|
|
|
534343 |
resetEvents();
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_KEY_DOWN]);
|
|
|
534343 |
heliArrayClear(&keyEvents[KEYEVENT_MOUSE_DOWN]);
|
|
|
a20939 |
|
|
|
534343 |
prevFrameTimeMs = SDL_GetTicks();
|
|
|
534343 |
heliDrawingPrepareFrame();
|
|
|
dba3fc |
|
|
|
dba3fc |
return success;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
a20939 |
|
|
|
a20939 |
static void run() {
|
|
|
a20939 |
while(!stopped) {
|
|
|
a20939 |
SDL_Event event;
|
|
|
a20939 |
while (SDL_PollEvent(&event))
|
|
|
a20939 |
handleEvent(&event);
|
|
|
a20939 |
draw();
|
|
|
a20939 |
}
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
a20939 |
|
|
|
d7d433 |
void windowRun() {
|
|
|
3f9996 |
if (started) return;
|
|
|
3f9996 |
started = TRUE;
|
|
|
3f9996 |
stopped = FALSE;
|
|
|
a20939 |
|
|
|
8bc1f1 |
firstFrame = TRUE;
|
|
|
3f9996 |
frameCount = 0;
|
|
|
a20939 |
elapsedTimeUs = 0;
|
|
|
a20939 |
elapsedTimeSinceLastFrameUs = 0;
|
|
|
3f9996 |
srand(time(NULL));
|
|
|
1015c5 |
|
|
|
a20939 |
if (init()) {
|
|
|
a20939 |
heliInitialized = TRUE;
|
|
|
a20939 |
|
|
|
3e7c5f |
resetState();
|
|
|
da4619 |
heliDoTests();
|
|
|
da4619 |
|
|
|
aaf750 |
viewportByWindow();
|
|
|
aaf750 |
projectionByViewport();
|
|
|
aaf750 |
heliDrawingPrepareFrame();
|
|
|
aaf750 |
|
|
|
aaf750 |
if (initCallback) initCallback();
|
|
|
a20939 |
run();
|
|
|
a20939 |
if (deinitCallback) deinitCallback();
|
|
|
a20939 |
|
|
|
a20939 |
heliArrayClear(&heliObjectsSet);
|
|
|
a20939 |
heliSpriteFinish();
|
|
|
a20939 |
heliDrawingFinish();
|
|
|
a20939 |
heliFontFinish();
|
|
|
a20939 |
heliAnimationFinish();
|
|
|
a20939 |
heliSoundFinish();
|
|
|
a20939 |
heliArrayDestroy(&heliObjectsSet);
|
|
|
a20939 |
|
|
|
a20939 |
heliInitialized = FALSE;
|
|
|
a20939 |
|
|
|
a20939 |
deinit();
|
|
|
a20939 |
|
|
|
aaf750 |
_mouseScrolledX = _mouseScrolledY = 0;
|
|
|
a20939 |
for(int i = 0; i < keyEventsCount; ++i)
|
|
|
a20939 |
heliArrayDestroy(&keyEvents[i]);
|
|
|
a20939 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
started = FALSE;
|
|
|
3f9996 |
}
|