|
|
3f9996 |
|
|
|
3f9996 |
#include <glib.h></glib.h>
|
|
|
3f9996 |
#include <gtk gtk.h=""></gtk>
|
|
|
3f9996 |
#include <gdk gdk.h=""></gdk>
|
|
|
3f9996 |
|
|
|
3f9996 |
#include "private.h"
|
|
|
3f9996 |
|
|
|
3f9996 |
#include "world.h"
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
cairo_t *heliCairo;
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
static GtkWidget *window;
|
|
|
3f9996 |
|
|
|
3f9996 |
static int started;
|
|
|
3f9996 |
static int stopped;
|
|
|
3f9996 |
static unsigned long long frameCount;
|
|
|
3f9996 |
static gint64 startTime;
|
|
|
3f9996 |
static gint64 currentTime;
|
|
|
3f9996 |
static guint timerInterval;
|
|
|
3f9996 |
|
|
|
3f9996 |
static Callback initCallback;
|
|
|
3f9996 |
static Callback drawCallback;
|
|
|
3f9996 |
|
|
|
3f9996 |
static int width;
|
|
|
3f9996 |
static int height;
|
|
|
3f9996 |
static double frameRate = 30;
|
|
|
3f9996 |
|
|
|
3f9996 |
static int cameraEnabled;
|
|
|
3f9996 |
static double cameraX;
|
|
|
3f9996 |
static double cameraY;
|
|
|
3f9996 |
static double cameraZoom = 1;
|
|
|
3f9996 |
|
|
|
3f9996 |
static HeliArray keysPressed;
|
|
|
3f9996 |
static HeliArray keysPressedInFrame;
|
|
|
3f9996 |
static HeliArray keysReleasedInFrame;
|
|
|
3f9996 |
static HeliArray buttonsPressed;
|
|
|
3f9996 |
static HeliArray buttonsPressedInFrame;
|
|
|
3f9996 |
static HeliArray buttonsReleasedInFrame;
|
|
|
3f9996 |
|
|
|
3f9996 |
static int mouseMovedInFrame;
|
|
|
3f9996 |
static double _mouseX;
|
|
|
3f9996 |
static double _mouseY;
|
|
|
3f9996 |
|
|
|
3f9996 |
static char* buttonNames[] = {
|
|
|
3f9996 |
"left",
|
|
|
3f9996 |
"middle",
|
|
|
3f9996 |
"right" };
|
|
|
3f9996 |
static int buttonsCount = (int)(sizeof(buttonNames)/sizeof(*buttonNames));
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
void playSound(char *path, int loop) {
|
|
|
3f9996 |
#warning "TODO: playSound: not implemented yet"
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
void stopSound(char *path) {
|
|
|
3f9996 |
#warning "TODO: stopSound: not implemented yet"
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
int keyDown(char *code)
|
|
|
3f9996 |
{ return heliStringsetGet(&keysPressed, code); }
|
|
|
3f9996 |
int keyWentDown(char *code);
|
|
|
3f9996 |
{ return heliStringsetGet(&keysPressedInFrame, code); }
|
|
|
3f9996 |
int keyWentUp(char *code);
|
|
|
3f9996 |
{ return heliStringsetGet(&keysReleasedInFrame, code); }
|
|
|
3f9996 |
|
|
|
3f9996 |
int mouseDidMove()
|
|
|
3f9996 |
{ return mouseMovedInFrame; }
|
|
|
3f9996 |
int mouseDown(char *code)
|
|
|
3f9996 |
{ return heliStringsetGet(&buttonsPressed, code); }
|
|
|
3f9996 |
int mouseWentDown(char *code)
|
|
|
3f9996 |
{ return heliStringsetGet(&buttonsPressedInFrame, code); }
|
|
|
3f9996 |
int mouseWentUp(char *code)
|
|
|
3f9996 |
{ return heliStringsetGet(&buttonsReleasedInFrame, code); }
|
|
|
3f9996 |
double mouseX()
|
|
|
3f9996 |
{ return _mouseX; }
|
|
|
3f9996 |
double mouseY()
|
|
|
3f9996 |
{ return _mouseY; }
|
|
|
3f9996 |
|
|
|
3f9996 |
int mousePressedOver(Sprite sprite)
|
|
|
3f9996 |
{ return buttonsPressed.count && int mouseIsOver(sprite); }
|
|
|
3f9996 |
|
|
|
3f9996 |
int worldGetWidth()
|
|
|
3f9996 |
{ return width; }
|
|
|
3f9996 |
void worldSetWidth(int w) {
|
|
|
3f9996 |
width = w;
|
|
|
3f9996 |
if (started) gtk_widget_set_size_request(window, width, height);
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
int worldGetHeight();
|
|
|
3f9996 |
{ return height; }
|
|
|
3f9996 |
void worldSetHeight(int h) {
|
|
|
3f9996 |
height = h;
|
|
|
3f9996 |
if (started) gtk_widget_set_size_request(window, width, height);
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
double worldGetFrameRate()
|
|
|
3f9996 |
{ return frameRate; }
|
|
|
3f9996 |
void worldSetFrameRate(double frameRate) {
|
|
|
3f9996 |
if (!(frameRate > 1)) frameRate = 1;
|
|
|
3f9996 |
if (!(frameRate < 100)) frameRate = 100;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
int worldGetFrameCount()
|
|
|
3f9996 |
{ return (int)frameCount; }
|
|
|
3f9996 |
double worldGetSeconds()
|
|
|
3f9996 |
{ return started ? (currentTime - startTime)*1e-6 : 0.0; }
|
|
|
3f9996 |
|
|
|
3f9996 |
void worldSetInit(Callback init)
|
|
|
3f9996 |
{ initCallback = init; }
|
|
|
3f9996 |
void worldSetDraw(Callback draw)
|
|
|
3f9996 |
{ drawCallback = draw; }
|
|
|
3f9996 |
void worldStop()
|
|
|
3f9996 |
{ if (started) stopped = TRUE; }
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
void cameraOn()
|
|
|
3f9996 |
{ cameraEnabled = TRUE; }
|
|
|
3f9996 |
void cameraOff()
|
|
|
3f9996 |
{ cameraEnabled = FALSE; }
|
|
|
3f9996 |
int cameraIsActive()
|
|
|
3f9996 |
{ return cameraEnabled; }
|
|
|
3f9996 |
|
|
|
3f9996 |
double cameraMouseX()
|
|
|
3f9996 |
{ return _mouseX/cameraZoom + cameraX; }
|
|
|
3f9996 |
double cameraMouseY()
|
|
|
3f9996 |
{ return _mouseY/cameraZoom + cameraY; }
|
|
|
3f9996 |
|
|
|
3f9996 |
double cameraGetX()
|
|
|
3f9996 |
{ return cameraX; }
|
|
|
3f9996 |
void cameraSetX(double x)
|
|
|
3f9996 |
{ cameraX = x; }
|
|
|
3f9996 |
|
|
|
3f9996 |
double cameraGetY()
|
|
|
3f9996 |
{ return cameraY; }
|
|
|
3f9996 |
void cameraSetY(double y)
|
|
|
3f9996 |
{ cameraY = y; }
|
|
|
3f9996 |
|
|
|
3f9996 |
double cameraGetZoom()
|
|
|
3f9996 |
{ return cameraZoom; }
|
|
|
3f9996 |
void cameraSetZoom(double x)
|
|
|
3f9996 |
{ cameraZoom = x > 1e-6 ? x : 1e-6; }
|
|
|
3f9996 |
|
|
|
3f9996 |
|
|
|
3f9996 |
static gboolean timeout(gpointer data G_GNUC_UNUSED) {
|
|
|
3f9996 |
gtk_widget_queue_draw(window);
|
|
|
3f9996 |
if (stopped) {
|
|
|
3f9996 |
gtk_window_close(GTK_WINDOW(window));
|
|
|
3f9996 |
return FALSE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
guint interval = (guint)round(1000/frameRate);
|
|
|
3f9996 |
if (interval != timerInterval) {
|
|
|
3f9996 |
timerInterval = interval;
|
|
|
3f9996 |
g_timeout_add(timerInterval, timeout, NULL);
|
|
|
3f9996 |
return FALSE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
static gboolean draw(GtkWidget *widget G_GNUC_UNUSED, cairo_t *cr, gpointer data G_GNUC_UNUSED) {
|
|
|
3f9996 |
guint64 newTime = g_get_monotonic_time();
|
|
|
3f9996 |
heliCairo = cr;
|
|
|
3f9996 |
|
|
|
3f9996 |
if (!frameCount) {
|
|
|
3f9996 |
startTime = currentTime = newTime;
|
|
|
3f9996 |
if (initCallback) initCallback();
|
|
|
3f9996 |
if (drawCallback) drawCallback();
|
|
|
3f9996 |
heliArrayClear(&keysPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&keysReleasedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&buttonsPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&buttonsReleasedInFrame, TRUE);
|
|
|
3f9996 |
++frameCount;
|
|
|
3f9996 |
} else {
|
|
|
3f9996 |
double dt = 1/frameRate;
|
|
|
3f9996 |
guint64 timeStep = (guint64)round(dt*1e6);
|
|
|
3f9996 |
while(currentTime < newTime) {
|
|
|
3f9996 |
currentTime += timeStep;
|
|
|
3f9996 |
++frameCount;
|
|
|
3f9996 |
heliUpdateSprites(dt);
|
|
|
3f9996 |
if (drawCallback) drawCallback();
|
|
|
3f9996 |
heliArrayClear(&keysPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&keysReleasedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&buttonsPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayClear(&buttonsReleasedInFrame, TRUE);
|
|
|
3f9996 |
}
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
heliCairo = NULL;
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
static gboolean handleEvent(GtkWidget *widget G_GNUC_UNUSED, GdkEvent *event, gpointer data G_GNUC_UNUSED) {
|
|
|
3f9996 |
gchar *keyname;
|
|
|
3f9996 |
int button;
|
|
|
3f9996 |
|
|
|
3f9996 |
switch(event->type) {
|
|
|
3f9996 |
case GDK_KEY_PRESS:
|
|
|
3f9996 |
keyname = heliStringCopy( gdk_keyval_name(event->key.keyval) );
|
|
|
3f9996 |
heliLowercase(keyname);
|
|
|
3f9996 |
heliStringsetAdd(&keysPressed, keyname);
|
|
|
3f9996 |
heliStringsetAdd(&keysPressedInFrame, keyname);
|
|
|
3f9996 |
free(keyname);
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
case GDK_KEY_RELEASE:
|
|
|
3f9996 |
keyname = heliStringCopy( gdk_keyval_name(event->key.keyval) );
|
|
|
3f9996 |
heliLowercase(keyname);
|
|
|
3f9996 |
heliStringsetRemove(&keysPressed, keyname);
|
|
|
3f9996 |
heliStringsetAdd(&keysReleasedInFrame, keyname);
|
|
|
3f9996 |
free(keyname);
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
case GDK_BUTTON_PRESS:
|
|
|
3f9996 |
button = event->button.button;
|
|
|
3f9996 |
if (button >= 0 && button < buttonsCount) {
|
|
|
3f9996 |
heliStringsetAdd(&buttonsPressed, buttonNames[button]);
|
|
|
3f9996 |
heliStringsetAdd(&buttonsPressedInFrame, buttonNames[button]);
|
|
|
3f9996 |
}
|
|
|
3f9996 |
_mouseX = event->button.x;
|
|
|
3f9996 |
_mouseY = event->button.y;
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
case GDK_BUTTON_RELEASE:
|
|
|
3f9996 |
button = event->button.button;
|
|
|
3f9996 |
if (button >= 0 && button < buttonsCount) {
|
|
|
3f9996 |
heliStringsetRemove(&buttonsPressed, buttonNames[button]);
|
|
|
3f9996 |
heliStringsetAdd(&buttonsReleasedInFrame, buttonNames[button]);
|
|
|
3f9996 |
}
|
|
|
3f9996 |
_mouseX = event->button.x;
|
|
|
3f9996 |
_mouseY = event->button.y;
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
case GDK_MOTION_NOTIFY:
|
|
|
3f9996 |
_mouseX = event->motion.x;
|
|
|
3f9996 |
_mouseY = event->motion.y;
|
|
|
3f9996 |
return TRUE;
|
|
|
3f9996 |
default:
|
|
|
3f9996 |
break;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
return FALSE;
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
static void activate(GtkApplication* app, gpointer data G_GNUC_UNUSED) {
|
|
|
3f9996 |
window = gtk_application_window_new(app);
|
|
|
3f9996 |
g_signal_connect(window, "draw", G_CALLBACK(draw), NULL);
|
|
|
3f9996 |
g_signal_connect(window, "event", G_CALLBACK(handleEvent), NULL);
|
|
|
3f9996 |
gtk_widget_add_events( window,
|
|
|
3f9996 |
GDK_POINTER_MOTION_MASK
|
|
|
3f9996 |
| GDK_BUTTON_PRESS_MASK
|
|
|
3f9996 |
| GDK_BUTTON_RELEASE_MASK
|
|
|
3f9996 |
| GDK_KEY_PRESS_MASK
|
|
|
3f9996 |
| GDK_KEY_RELEASE_MASK );
|
|
|
3f9996 |
gtk_widget_set_size_request(window, width, height);
|
|
|
3f9996 |
gtk_widget_show(window);
|
|
|
3f9996 |
timerInterval = (guint)round(1000/frameRate);
|
|
|
3f9996 |
g_timeout_add(timerInterval, timeout, NULL);
|
|
|
3f9996 |
if (initCallback) initCallback();
|
|
|
3f9996 |
}
|
|
|
3f9996 |
|
|
|
3f9996 |
void worldRun() {
|
|
|
3f9996 |
if (started) return;
|
|
|
3f9996 |
started = TRUE;
|
|
|
3f9996 |
stopped = FALSE;
|
|
|
3f9996 |
|
|
|
3f9996 |
frameCount = 0;
|
|
|
3f9996 |
srand(time(NULL));
|
|
|
3f9996 |
|
|
|
3f9996 |
GtkApplication *application = gtk_application_new(NULL, 0);
|
|
|
3f9996 |
g_signal_connect(application, "activate", G_CALLBACK(activate), NULL);
|
|
|
3f9996 |
g_application_run(G_APPLICATION(application), 0, NULL);
|
|
|
3f9996 |
g_object_unref(application);
|
|
|
3f9996 |
|
|
|
3f9996 |
while(worldGetSpriteCount())
|
|
|
3f9996 |
{ spriteDestroy(worldGetSprite(0)); }
|
|
|
3f9996 |
|
|
|
3f9996 |
heliArrayDestroy(&keysPressed, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&keysPressed, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&keysPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&keysReleasedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&buttonsPressed, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&buttonsPressedInFrame, TRUE);
|
|
|
3f9996 |
heliArrayDestroy(&buttonsReleasedInFrame, TRUE);
|
|
|
3f9996 |
|
|
|
3f9996 |
started = FALSE;
|
|
|
3f9996 |
}
|