Ver código fonte

Properly implement program termination, including in the GTK program.

legacy
Sam Hocevar sam 15 anos atrás
pai
commit
d5ffa9fe4f
15 arquivos alterados com 186 adições e 73 exclusões
  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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

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

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

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


+ 2
- 6
src/entity.h Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

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

protected:


+ 2
- 2
src/test-map.cpp Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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 Ver arquivo

@@ -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()


Carregando…
Cancelar
Salvar