Blame heliantus/world.c

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
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
8535a3
void playSound(const char *path, int loop) {
3f9996
	#warning "TODO: playSound: not implemented yet"
3f9996
}
3f9996
8535a3
void stopSound(const char *path) {
3f9996
	#warning "TODO: stopSound: not implemented yet"
3f9996
}
3f9996
8535a3
int keyDown(const char *code)
8535a3
	{ return heliStringmapGet(&keysPressed, code) != NULL; }
07b70f
int keyWentDown(const char *code)
8535a3
	{ return heliStringmapGet(&keysPressedInFrame, code) != NULL; }
07b70f
int keyWentUp(const char *code)
8535a3
	{ return heliStringmapGet(&keysReleasedInFrame, code) != NULL; }
3f9996
3f9996
int mouseDidMove()
3f9996
	{ return mouseMovedInFrame; }
8535a3
int mouseDown(const char *code)
8535a3
	{ return heliStringmapGet(&buttonsPressed, code) != NULL; }
8535a3
int mouseWentDown(const char *code)
8535a3
	{ return heliStringmapGet(&buttonsPressedInFrame, code) != NULL; }
8535a3
int mouseWentUp(const char *code)
8535a3
	{ return heliStringmapGet(&buttonsReleasedInFrame, code) != NULL; }
3f9996
double mouseX()
3f9996
	{ return _mouseX; }
3f9996
double mouseY()
3f9996
	{ return _mouseY; }
3f9996
3f9996
int mousePressedOver(Sprite sprite)
8535a3
	{ return buttonsPressed.count && 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
07b70f
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();
07b70f
		heliDrawingPrepareFrame();
3f9996
		if (drawCallback) drawCallback();
8535a3
		heliArrayClear(&keysPressedInFrame);
8535a3
		heliArrayClear(&keysReleasedInFrame);
8535a3
		heliArrayClear(&buttonsPressedInFrame);
8535a3
		heliArrayClear(&buttonsReleasedInFrame);
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;
07b70f
			heliSpriteUpdate(dt);
07b70f
			heliDrawingPrepareFrame();
3f9996
			if (drawCallback) drawCallback();
8535a3
			heliArrayClear(&keysPressedInFrame);
8535a3
			heliArrayClear(&keysReleasedInFrame);
8535a3
			heliArrayClear(&buttonsPressedInFrame);
8535a3
			heliArrayClear(&buttonsReleasedInFrame);
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);
8535a3
		heliStringmapAdd(&keysPressed, keyname, NULL, NULL);
8535a3
		heliStringmapAdd(&keysPressedInFrame, keyname, NULL, NULL);
3f9996
		free(keyname);
3f9996
		return TRUE;
3f9996
	case GDK_KEY_RELEASE:
3f9996
		keyname = heliStringCopy( gdk_keyval_name(event->key.keyval) );
3f9996
		heliLowercase(keyname);
8535a3
		heliStringmapRemove(&keysPressed, keyname);
8535a3
		heliStringmapAdd(&keysReleasedInFrame, keyname, NULL, NULL);
3f9996
		free(keyname);
3f9996
		return TRUE;
3f9996
	case GDK_BUTTON_PRESS:
3f9996
		button = event->button.button;
3f9996
		if (button >= 0 && button < buttonsCount) {
8535a3
			heliStringmapAdd(&buttonsPressed, buttonNames[button], NULL, NULL);
8535a3
			heliStringmapAdd(&buttonsPressedInFrame, buttonNames[button], NULL, NULL);
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) {
8535a3
			heliStringmapRemove(&buttonsPressed, buttonNames[button]);
8535a3
			heliStringmapAdd(&buttonsReleasedInFrame, buttonNames[button], NULL, NULL);
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
	
07b70f
	heliSpriteFinish();
07b70f
	heliDrawingFinish();
07b70f
	heliAnimationFinish();
3f9996
8535a3
	heliArrayDestroy(&keysPressed);
8535a3
	heliArrayDestroy(&keysPressed);
8535a3
	heliArrayDestroy(&keysPressedInFrame);
8535a3
	heliArrayDestroy(&keysReleasedInFrame);
8535a3
	heliArrayDestroy(&buttonsPressed);
8535a3
	heliArrayDestroy(&buttonsPressedInFrame);
8535a3
	heliArrayDestroy(&buttonsReleasedInFrame);
3f9996
	
3f9996
	started = FALSE;
3f9996
}