Browse Source

Properly implement program termination, including in the GTK program.

legacy
Sam Hocevar sam 16 years ago
parent
commit
d5ffa9fe4f
15 changed files with 186 additions and 73 deletions
  1. +2
    -0
      src/debugsprite.cpp
  2. +3
    -3
      src/dict.cpp
  3. +0
    -10
      src/entity.cpp
  4. +2
    -6
      src/entity.h
  5. +8
    -6
      src/font.cpp
  6. +14
    -3
      src/gtk/editor.cpp
  7. +6
    -6
      src/gtk/editor.xml
  8. +23
    -9
      src/gtk/glmapview.cpp
  9. +3
    -3
      src/gtk/glmapview.h
  10. +2
    -7
      src/sdlinput.cpp
  11. +1
    -1
      src/sdlinput.h
  12. +2
    -2
      src/test-map.cpp
  13. +107
    -11
      src/ticker.cpp
  14. +5
    -0
      src/ticker.h
  15. +8
    -6
      src/tileset.cpp

+ 2
- 0
src/debugsprite.cpp View File

@@ -35,6 +35,7 @@ DebugSprite::DebugSprite(Game *game)
{ {
data = new DebugSpriteData(); data = new DebugSpriteData();
data->game = game; data->game = game;
Ticker::Ref(game);
data->tiler = Tiler::Register("art/test/character-dress.png"); data->tiler = Tiler::Register("art/test/character-dress.png");
data->x = 320; data->x = 320;
data->y = 206; data->y = 206;
@@ -71,6 +72,7 @@ void DebugSprite::TickDraw(float deltams)


DebugSprite::~DebugSprite() DebugSprite::~DebugSprite()
{ {
Ticker::Unref(data->game);
Tiler::Deregister(data->tiler); Tiler::Deregister(data->tiler);
delete data; delete data;
} }


+ 3
- 3
src/dict.cpp View File

@@ -85,7 +85,7 @@ int Dict::MakeSlot(char const *name)
} }
else else
{ {
data->entities[id]->Ref();
Ticker::Ref(data->entities[id]);
} }


return id; return id;
@@ -93,14 +93,14 @@ int Dict::MakeSlot(char const *name)


void Dict::RemoveSlot(int id) void Dict::RemoveSlot(int id)
{ {
if (data->entities[id]->Unref() == 0)
if (Ticker::Unref(data->entities[id]) == 0)
data->entities[id] = NULL; data->entities[id] = NULL;
} }




void Dict::SetEntity(int id, Entity *entity) void Dict::SetEntity(int id, Entity *entity)
{ {
entity->Ref();
Ticker::Ref(entity);
data->entities[id] = entity; data->entities[id] = entity;
} }




+ 0
- 10
src/entity.cpp View File

@@ -63,13 +63,3 @@ void Entity::TickDraw(float deltams)
#endif #endif
} }


void Entity::Ref()
{
ref++;
}

int Entity::Unref()
{
return --ref;
}


+ 2
- 6
src/entity.h View File

@@ -22,10 +22,6 @@ class Entity
friend class TickerData; friend class TickerData;
friend class Dict; friend class Dict;


public:
virtual void Ref();
virtual int Unref();

protected: protected:
typedef enum typedef enum
{ {
@@ -47,8 +43,8 @@ protected:
virtual void TickGame(float deltams); virtual void TickGame(float deltams);
virtual void TickDraw(float deltams); virtual void TickDraw(float deltams);


Entity *next;
int ref, destroy;
Entity *next, *autonext;
int ref, autorelease, destroy;


#if !FINAL_RELEASE #if !FINAL_RELEASE
enum enum


+ 8
- 6
src/font.cpp View File

@@ -74,7 +74,14 @@ void Font::TickDraw(float deltams)
{ {
Entity::TickDraw(deltams); Entity::TickDraw(deltams);


if (data->img)
if (destroy)
{
if (data->img)
SDL_FreeSurface(data->img);
else
glDeleteTextures(1, &data->texture);
}
else if (data->img)
{ {
data->width = data->img->w / 16; data->width = data->img->w / 16;
data->height = data->img->h / 16; data->height = data->img->h / 16;
@@ -91,11 +98,6 @@ void Font::TickDraw(float deltams)
SDL_FreeSurface(data->img); SDL_FreeSurface(data->img);
data->img = NULL; data->img = NULL;
} }
else if (ref == 0)
{
glDeleteTextures(1, &data->texture);
destroy = 1;
}
} }


char const *Font::GetName() char const *Font::GetName()


+ 14
- 3
src/gtk/editor.cpp View File

@@ -16,6 +16,15 @@
#include "glmapview.h" #include "glmapview.h"
#include "debugfps.h" #include "debugfps.h"


static gboolean delayed_quit(GtkWidget *w, GdkEvent *e, void *data)
{
(void)w;
(void)e;
(void)data;
gtk_main_quit();
return TRUE;
}

int main(int argc, char **argv) int main(int argc, char **argv)
{ {
/* Initialize GTK */ /* Initialize GTK */
@@ -41,12 +50,14 @@ int main(int argc, char **argv)
GlMapView *glmapview = new GlMapView(builder); GlMapView *glmapview = new GlMapView(builder);


/* Show window. We're good to go! */ /* Show window. We're good to go! */
gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(builder, "window")));
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
gtk_widget_show_all(window);
gtk_signal_connect(GTK_OBJECT(window), "delete_event",
GTK_SIGNAL_FUNC(delayed_quit), NULL);
g_object_unref(G_OBJECT(builder)); g_object_unref(G_OBJECT(builder));


new DebugFps();
glmapview->LoadMap("maps/testmap.tmx"); glmapview->LoadMap("maps/testmap.tmx");
glmapview->SetFocus();
new DebugFps();


gtk_main(); gtk_main();




+ 6
- 6
src/gtk/editor.xml View File

@@ -4,7 +4,6 @@
<!-- interface-naming-policy project-wide --> <!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="window"> <object class="GtkWindow" id="window">
<property name="title" translatable="yes">Deus Hax Editor</property> <property name="title" translatable="yes">Deus Hax Editor</property>
<signal name="delete_event" handler="gtk_main_quit"/>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkVBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
@@ -23,8 +22,8 @@
<child> <child>
<object class="GtkImageMenuItem" id="imagemenuitem1"> <object class="GtkImageMenuItem" id="imagemenuitem1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="related_action">action_new</property>
<property name="use_action_appearance">True</property> <property name="use_action_appearance">True</property>
<property name="related_action">action_new</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
</object> </object>
@@ -32,8 +31,8 @@
<child> <child>
<object class="GtkImageMenuItem" id="imagemenuitem2"> <object class="GtkImageMenuItem" id="imagemenuitem2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="related_action">action_open</property>
<property name="use_action_appearance">True</property> <property name="use_action_appearance">True</property>
<property name="related_action">action_open</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
</object> </object>
@@ -67,6 +66,7 @@
<property name="use_stock">True</property> <property name="use_stock">True</property>
<property name="always_show_image">True</property> <property name="always_show_image">True</property>
<signal name="activate" handler="gtk_main_quit"/> <signal name="activate" handler="gtk_main_quit"/>
<signal name="activate" handler="gtk_true"/>
</object> </object>
</child> </child>
</object> </object>
@@ -158,8 +158,8 @@
<child> <child>
<object class="GtkToolButton" id="toolbutton2"> <object class="GtkToolButton" id="toolbutton2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="related_action">action_new</property>
<property name="use_action_appearance">True</property> <property name="use_action_appearance">True</property>
<property name="related_action">action_new</property>
<property name="label" translatable="yes">toolbutton</property> <property name="label" translatable="yes">toolbutton</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
</object> </object>
@@ -171,8 +171,8 @@
<child> <child>
<object class="GtkToolButton" id="toolbutton1"> <object class="GtkToolButton" id="toolbutton1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="related_action">action_open</property>
<property name="use_action_appearance">True</property> <property name="use_action_appearance">True</property>
<property name="related_action">action_open</property>
<property name="label" translatable="yes">Open...</property> <property name="label" translatable="yes">Open...</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
</object> </object>
@@ -184,8 +184,8 @@
<child> <child>
<object class="GtkToolButton" id="toolbutton4"> <object class="GtkToolButton" id="toolbutton4">
<property name="visible">True</property> <property name="visible">True</property>
<property name="related_action">action_save</property>
<property name="use_action_appearance">True</property> <property name="use_action_appearance">True</property>
<property name="related_action">action_save</property>
<property name="label" translatable="yes">Save</property> <property name="label" translatable="yes">Save</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
</object> </object>


+ 23
- 9
src/gtk/glmapview.cpp View File

@@ -50,11 +50,10 @@ GlMapView::GlMapView(GtkBuilder *builder)
* stealing time from the GTK loop when the callback time exceeds * stealing time from the GTK loop when the callback time exceeds
* the timeout value. */ * the timeout value. */
g_idle_add((GSourceFunc)IdleTickSignal, this); g_idle_add((GSourceFunc)IdleTickSignal, this);
gtk_quit_add(0, (GtkFunction)ShutdownSignal, this);


gtk_signal_connect(GTK_OBJECT(glarea), "realize", gtk_signal_connect(GTK_OBJECT(glarea), "realize",
GTK_SIGNAL_FUNC(SetupSignal), this); GTK_SIGNAL_FUNC(SetupSignal), this);
gtk_signal_connect(GTK_OBJECT(glarea), "destroy",
GTK_SIGNAL_FUNC(DestroySignal), this);
gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
GTK_SIGNAL_FUNC(DrawSignal), this); GTK_SIGNAL_FUNC(DrawSignal), this);
gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
@@ -75,17 +74,28 @@ void GlMapView::LoadMap(char const *path)
{ {
// FIXME: detect when the map viewer is killed // FIXME: detect when the map viewer is killed
mapviewer = new MapViewer(path); mapviewer = new MapViewer(path);
Ticker::Ref(mapviewer);


UpdateAdjustments(); UpdateAdjustments();
} }


void GlMapView::SetFocus()
void GlMapView::CloseMap()
{ {
gtk_widget_grab_focus(glarea);
if (mapviewer)
Ticker::Unref(mapviewer);
mapviewer = NULL;

UpdateAdjustments();
} }


gboolean GlMapView::IdleTick() gboolean GlMapView::IdleTick()
{ {
if (Ticker::Finished())
{
gtk_main_quit();
return FALSE;
}

// FIXME: do not do anything if the previous tick was too recent? // FIXME: do not do anything if the previous tick was too recent?
ticking = TRUE; ticking = TRUE;


@@ -104,6 +114,7 @@ gboolean GlMapView::IdleTick()
gboolean GlMapView::Setup() gboolean GlMapView::Setup()
{ {
/* Set up display */ /* Set up display */
gtk_widget_grab_focus(glarea);
if (gtk_gl_area_make_current(GTK_GL_AREA(glarea))) if (gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
Video::Setup(glarea->allocation.width, glarea->allocation.height); Video::Setup(glarea->allocation.width, glarea->allocation.height);


@@ -112,9 +123,13 @@ gboolean GlMapView::Setup()
return TRUE; return TRUE;
} }


gboolean GlMapView::Destroy()
gboolean GlMapView::Shutdown()
{ {
g_idle_remove_by_data(this);
CloseMap();
Ticker::Shutdown();
/* Hijack the exit process by adding another level of gtk_main */
gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE);
gtk_main();
return TRUE; return TRUE;
} }


@@ -239,10 +254,9 @@ gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
return that->Setup(); return that->Setup();
} }


gboolean GlMapView::DestroySignal(GtkWidget *w, GlMapView *that)
gboolean GlMapView::ShutdownSignal(GlMapView *that)
{ {
(void)w;
return that->Destroy();
return that->Shutdown();
} }


gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e, gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e,


+ 3
- 3
src/gtk/glmapview.h View File

@@ -13,13 +13,13 @@ class GlMapView
public: public:
GlMapView(GtkBuilder *builder); GlMapView(GtkBuilder *builder);
void LoadMap(char const *path); void LoadMap(char const *path);
void SetFocus();
void CloseMap();


private: private:
/* Private methods */ /* Private methods */
gboolean IdleTick(); gboolean IdleTick();
gboolean Setup(); gboolean Setup();
gboolean Destroy();
gboolean Shutdown();
gboolean Draw(GdkEventExpose *e); gboolean Draw(GdkEventExpose *e);
void Scroll(double dx, double dy); void Scroll(double dx, double dy);
void UpdateAdjustments(); void UpdateAdjustments();
@@ -30,7 +30,7 @@ private:
/* Private signal slots */ /* Private signal slots */
static gboolean IdleTickSignal(GlMapView *that); static gboolean IdleTickSignal(GlMapView *that);
static gboolean SetupSignal(GtkWidget *w, GlMapView *that); static gboolean SetupSignal(GtkWidget *w, GlMapView *that);
static gboolean DestroySignal(GtkWidget *w, GlMapView *that);
static gboolean ShutdownSignal(GlMapView *that);
static gboolean DrawSignal(GtkWidget *w, GdkEventExpose *e, static gboolean DrawSignal(GtkWidget *w, GdkEventExpose *e,
GlMapView *that); GlMapView *that);
static gboolean ReshapeSignal(GtkWidget *w, GdkEventConfigure *e, static gboolean ReshapeSignal(GtkWidget *w, GdkEventConfigure *e,


+ 2
- 7
src/sdlinput.cpp View File

@@ -21,7 +21,6 @@ class SdlInputData
friend class SdlInput; friend class SdlInput;


private: private:
Game *game;
int mx, my; int mx, my;
}; };


@@ -29,12 +28,11 @@ private:
* Public SdlInput class * Public SdlInput class
*/ */


SdlInput::SdlInput(Game *game)
SdlInput::SdlInput()
{ {
SDL_Init(SDL_INIT_TIMER); SDL_Init(SDL_INIT_TIMER);


data = new SdlInputData(); data = new SdlInputData();
data->game = game;
SDL_GetMouseState(&data->mx, &data->my); SDL_GetMouseState(&data->mx, &data->my);
} }


@@ -47,9 +45,6 @@ void SdlInput::TickGame(float deltams)
{ {
Entity::TickGame(deltams); Entity::TickGame(deltams);


if (data->game->Finished())
destroy = 1;

/* Handle mouse input */ /* Handle mouse input */
SDL_GetMouseState(&data->mx, &data->my); SDL_GetMouseState(&data->mx, &data->my);


@@ -58,7 +53,7 @@ void SdlInput::TickGame(float deltams)
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event))
{ {
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
data->game->Quit();
Ticker::Shutdown();
#if 0 #if 0
else if (event.type == SDL_KEYDOWN) else if (event.type == SDL_KEYDOWN)
Input::KeyPressed(event.key.keysym.sym, deltams); Input::KeyPressed(event.key.keysym.sym, deltams);


+ 1
- 1
src/sdlinput.h View File

@@ -19,7 +19,7 @@ class SdlInputData;
class SdlInput : public Entity class SdlInput : public Entity
{ {
public: public:
SdlInput(Game *game);
SdlInput();
virtual ~SdlInput(); virtual ~SdlInput();


protected: protected:


+ 2
- 2
src/test-map.cpp View File

@@ -48,12 +48,12 @@ int main(int argc, char **argv)
Game *game = new Game("maps/testmap.tmx"); Game *game = new Game("maps/testmap.tmx");


/* Register an input driver and some debug stuff */ /* Register an input driver and some debug stuff */
new SdlInput(game);
new SdlInput();
new DebugFps(); new DebugFps();
new DebugSprite(game); new DebugSprite(game);
//new DebugRecord("lolengine.ogg"); //new DebugRecord("lolengine.ogg");


while (!game->Finished())
while (!Ticker::Finished())
{ {
/* Tick the game */ /* Tick the game */
Ticker::TickGame(); Ticker::TickGame();


+ 107
- 11
src/ticker.cpp View File

@@ -23,7 +23,8 @@ static class TickerData


public: public:
TickerData() : TickerData() :
todo(0), nentities(0),
todolist(0), autolist(0),
nentities(0),
frame(0), deltams(0), bias(0) frame(0), deltams(0), bias(0)
{ {
for (int i = 0; i < Entity::GROUP_COUNT; i++) for (int i = 0; i < Entity::GROUP_COUNT; i++)
@@ -35,17 +36,26 @@ public:
#if !FINAL_RELEASE #if !FINAL_RELEASE
if (nentities) if (nentities)
fprintf(stderr, "ERROR: still %i entities in ticker\n", nentities); fprintf(stderr, "ERROR: still %i entities in ticker\n", nentities);
if (autolist)
{
int count = 0;
for (Entity *e = autolist; e; e = e->autonext, count++)
;
fprintf(stderr, "ERROR: still %i autoreleased entities\n", count);
}
fprintf(stderr, "INFO: %i frames required to quit\n",
frame - quitframe);
#endif #endif
} }


private: private:
/* Entity management */ /* Entity management */
Entity *todo;
Entity *todolist, *autolist;
Entity *list[Entity::GROUP_COUNT]; Entity *list[Entity::GROUP_COUNT];
int nentities; int nentities;


/* Fixed framerate management */ /* Fixed framerate management */
int frame;
int frame, quitframe;
Timer timer; Timer timer;
float deltams, bias; float deltams, bias;
} }
@@ -62,8 +72,55 @@ void Ticker::Register(Entity *entity)
/* If we are called from its constructor, the object's vtable is not /* If we are called from its constructor, the object's vtable is not
* ready yet, so we do not know which group this entity belongs to. Wait * ready yet, so we do not know which group this entity belongs to. Wait
* until the first tick. */ * until the first tick. */
entity->next = data->todo;
data->todo = entity;
entity->next = data->todolist;
data->todolist = entity;
/* Objects are autoreleased by default. Put them in a circular list. */
entity->autorelease = 1;
entity->autonext = data->autolist;
data->autolist = entity;
entity->ref = 1;

data->nentities++;
}

void Ticker::Ref(Entity *entity)
{
#if !FINAL_RELEASE
if (entity->destroy)
fprintf(stderr, "ERROR: refing entity scheduled for destruction\n");
#endif
if (entity->autorelease)
{
/* Get the entity out of the autorelease list. This is usually
* very fast since the first entry in autolist is the last
* registered entity. */
for (Entity *e = data->autolist, *prev = NULL; e;
prev = e, e = e->autonext)
{
if (e == entity)
{
if (prev)
prev->autonext = e->autonext;
else
data->autolist = e->autonext;
break;
}
}
entity->autorelease = 0;
}
else
entity->ref++;
}

int Ticker::Unref(Entity *entity)
{
#if !FINAL_RELEASE
if (entity->ref <= 0)
fprintf(stderr, "ERROR: dereferencing unreferenced entity\n");
if (entity->autorelease)
fprintf(stderr, "ERROR: dereferencing autoreleased entity\n");
#endif
return --entity->ref;
} }


void Ticker::TickGame() void Ticker::TickGame()
@@ -73,6 +130,17 @@ void Ticker::TickGame()


Profiler::Start(Profiler::STAT_TICK_GAME); Profiler::Start(Profiler::STAT_TICK_GAME);


#if 0
fprintf(stderr, "-------------------------------------\n");
for (int i = 0; i < Entity::GROUP_COUNT; i++)
{
fprintf(stderr, "Group %i\n", i);

for (Entity *e = data->list[i]; e; e = e->next)
fprintf(stderr, " \\-- %s (ref %i, destroy %i)\n", e->GetName(), e->ref, e->destroy);
}
#endif

data->frame++; data->frame++;


data->deltams = data->timer.GetMs(); data->deltams = data->timer.GetMs();
@@ -82,7 +150,8 @@ void Ticker::TickGame()
* before inserting awaiting objects, because there is no way these * before inserting awaiting objects, because there is no way these
* are already marked for destruction. */ * are already marked for destruction. */
for (int i = 0; i < Entity::GROUP_COUNT; i++) for (int i = 0; i < Entity::GROUP_COUNT; i++)
for (Entity *e = data->list[i], *prev = NULL; e; prev = e, e = e->next)
for (Entity *e = data->list[i], *prev = NULL; e; )
{
if (e->destroy) if (e->destroy)
{ {
if (prev) if (prev)
@@ -90,20 +159,30 @@ void Ticker::TickGame()
else else
data->list[i] = e->next; data->list[i] = e->next;


Entity *tmp = e;
e = e->next;
delete tmp;

data->nentities--; data->nentities--;
delete e;
} }
else
{
if (e->ref <= 0)
e->destroy = 1;
prev = e;
e = e->next;
}
}


/* Insert waiting objects into the appropriate lists */ /* Insert waiting objects into the appropriate lists */
while (data->todo)
while (data->todolist)
{ {
Entity *e = data->todo;
data->todo = e->next;
Entity *e = data->todolist;
data->todolist = e->next;


int i = e->GetGroup(); int i = e->GetGroup();
e->next = data->list[i]; e->next = data->list[i];
data->list[i] = e; data->list[i] = e;
data->nentities++;
} }


/* Tick objects for the game loop */ /* Tick objects for the game loop */
@@ -169,3 +248,20 @@ int Ticker::GetFrameNum()
return data->frame; return data->frame;
} }


void Ticker::Shutdown()
{
/* We're bailing out. Release all autorelease objects. */
while (data->autolist)
{
data->autolist->ref--;
data->autolist = data->autolist->autonext;
}

data->quitframe = data->frame;
}

int Ticker::Finished()
{
return !data->nentities;
}


+ 5
- 0
src/ticker.h View File

@@ -20,11 +20,16 @@ class Ticker
{ {
public: public:
static void Register(Entity *entity); static void Register(Entity *entity);
static void Ref(Entity *entity);
static int Unref(Entity *entity);


static void TickGame(); static void TickGame();
static void TickDraw(); static void TickDraw();
static void ClampFps(float deltams); static void ClampFps(float deltams);
static int GetFrameNum(); static int GetFrameNum();

static void Shutdown();
static int Finished();
}; };


#endif // __DH_TICKER_H__ #endif // __DH_TICKER_H__


+ 8
- 6
src/tileset.cpp View File

@@ -83,7 +83,14 @@ void TileSet::TickDraw(float deltams)
{ {
Entity::TickDraw(deltams); Entity::TickDraw(deltams);


if (data->img)
if (destroy)
{
if (data->img)
SDL_FreeSurface(data->img);
else
glDeleteTextures(1, &data->texture);
}
else if (data->img)
{ {
glGenTextures(1, &data->texture); glGenTextures(1, &data->texture);
glBindTexture(GL_TEXTURE_2D, data->texture); glBindTexture(GL_TEXTURE_2D, data->texture);
@@ -97,11 +104,6 @@ void TileSet::TickDraw(float deltams)
SDL_FreeSurface(data->img); SDL_FreeSurface(data->img);
data->img = NULL; data->img = NULL;
} }
else if (ref == 0)
{
glDeleteTextures(1, &data->texture);
destroy = 1;
}
} }


char const *TileSet::GetName() char const *TileSet::GetName()


Loading…
Cancel
Save