Browse Source

Properly implement program termination, including in the GTK program.

legacy
Sam Hocevar sam 14 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->game = game;
Ticker::Ref(game);
data->tiler = Tiler::Register("art/test/character-dress.png");
data->x = 320;
data->y = 206;
@@ -71,6 +72,7 @@ void DebugSprite::TickDraw(float deltams)

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


+ 3
- 3
src/dict.cpp View File

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

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

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


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



+ 0
- 10
src/entity.cpp View File

@@ -63,13 +63,3 @@ void Entity::TickDraw(float deltams)
#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 Dict;

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

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

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

#if !FINAL_RELEASE
enum


+ 8
- 6
src/font.cpp View File

@@ -74,7 +74,14 @@ void Font::TickDraw(float 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->height = data->img->h / 16;
@@ -91,11 +98,6 @@ void Font::TickDraw(float deltams)
SDL_FreeSurface(data->img);
data->img = NULL;
}
else if (ref == 0)
{
glDeleteTextures(1, &data->texture);
destroy = 1;
}
}

char const *Font::GetName()


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

@@ -16,6 +16,15 @@
#include "glmapview.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)
{
/* Initialize GTK */
@@ -41,12 +50,14 @@ int main(int argc, char **argv)
GlMapView *glmapview = new GlMapView(builder);

/* 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));

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

gtk_main();



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

@@ -4,7 +4,6 @@
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Deus Hax Editor</property>
<signal name="delete_event" handler="gtk_main_quit"/>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
@@ -23,8 +22,8 @@
<child>
<object class="GtkImageMenuItem" id="imagemenuitem1">
<property name="visible">True</property>
<property name="related_action">action_new</property>
<property name="use_action_appearance">True</property>
<property name="related_action">action_new</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
@@ -32,8 +31,8 @@
<child>
<object class="GtkImageMenuItem" id="imagemenuitem2">
<property name="visible">True</property>
<property name="related_action">action_open</property>
<property name="use_action_appearance">True</property>
<property name="related_action">action_open</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
@@ -67,6 +66,7 @@
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="gtk_main_quit"/>
<signal name="activate" handler="gtk_true"/>
</object>
</child>
</object>
@@ -158,8 +158,8 @@
<child>
<object class="GtkToolButton" id="toolbutton2">
<property name="visible">True</property>
<property name="related_action">action_new</property>
<property name="use_action_appearance">True</property>
<property name="related_action">action_new</property>
<property name="label" translatable="yes">toolbutton</property>
<property name="use_underline">True</property>
</object>
@@ -171,8 +171,8 @@
<child>
<object class="GtkToolButton" id="toolbutton1">
<property name="visible">True</property>
<property name="related_action">action_open</property>
<property name="use_action_appearance">True</property>
<property name="related_action">action_open</property>
<property name="label" translatable="yes">Open...</property>
<property name="use_underline">True</property>
</object>
@@ -184,8 +184,8 @@
<child>
<object class="GtkToolButton" id="toolbutton4">
<property name="visible">True</property>
<property name="related_action">action_save</property>
<property name="use_action_appearance">True</property>
<property name="related_action">action_save</property>
<property name="label" translatable="yes">Save</property>
<property name="use_underline">True</property>
</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
* the timeout value. */
g_idle_add((GSourceFunc)IdleTickSignal, this);
gtk_quit_add(0, (GtkFunction)ShutdownSignal, this);

gtk_signal_connect(GTK_OBJECT(glarea), "realize",
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_FUNC(DrawSignal), this);
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
mapviewer = new MapViewer(path);
Ticker::Ref(mapviewer);

UpdateAdjustments();
}

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

UpdateAdjustments();
}

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

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

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

@@ -112,9 +123,13 @@ gboolean GlMapView::Setup()
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;
}

@@ -239,10 +254,9 @@ gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
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,


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

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

private:
/* Private methods */
gboolean IdleTick();
gboolean Setup();
gboolean Destroy();
gboolean Shutdown();
gboolean Draw(GdkEventExpose *e);
void Scroll(double dx, double dy);
void UpdateAdjustments();
@@ -30,7 +30,7 @@ private:
/* Private signal slots */
static gboolean IdleTickSignal(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,
GlMapView *that);
static gboolean ReshapeSignal(GtkWidget *w, GdkEventConfigure *e,


+ 2
- 7
src/sdlinput.cpp View File

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

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

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

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

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

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

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

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

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


+ 1
- 1
src/sdlinput.h View File

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

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");

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

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


+ 107
- 11
src/ticker.cpp View File

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

public:
TickerData() :
todo(0), nentities(0),
todolist(0), autolist(0),
nentities(0),
frame(0), deltams(0), bias(0)
{
for (int i = 0; i < Entity::GROUP_COUNT; i++)
@@ -35,17 +36,26 @@ public:
#if !FINAL_RELEASE
if (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
}

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

/* Fixed framerate management */
int frame;
int frame, quitframe;
Timer timer;
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
* ready yet, so we do not know which group this entity belongs to. Wait
* 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()
@@ -73,6 +130,17 @@ void Ticker::TickGame()

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

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

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

/* 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();
e->next = data->list[i];
data->list[i] = e;
data->nentities++;
}

/* Tick objects for the game loop */
@@ -169,3 +248,20 @@ int Ticker::GetFrameNum()
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:
static void Register(Entity *entity);
static void Ref(Entity *entity);
static int Unref(Entity *entity);

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

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

#endif // __DH_TICKER_H__


+ 8
- 6
src/tileset.cpp View File

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

char const *TileSet::GetName()


Loading…
Cancel
Save