Agar

Hypertriton, Inc. Hypertriton, Inc.
( Francais )
HOME | SCREENSHOTS | DOWNLOAD | DOCS | SUPPORT | CHAT | CONTRIBUTE | REPORT BUG
Introduction

The flow of instructions in a GUI application is usually such that, after some initialization steps, a body of code called the event loop (or main loop) is executed repeatedly, until the application terminates. For instance, an event loop could update the display, process events (e.g., input device events, conditions on network sockets or simply the passage of time).

Agar provides one trivial event loop routine called AG_EventLoop_FixedFPS() (alias AG_EventLoop()) that is suitable to most typical applications. It periodically updates the display, updates active timers and relays SDL events to the GUI with AG_ProcessEvent(). Using SDL_GetTicks(), it estimates the amount of time spent and readjusts the delays in order to maintain a fixed refresh rate. If that target refresh rate is met, extra time is spent idling such that no CPU is wasted. This algorithm performs more accurately as you increase the system clock frequency.

Custom event loops

You don't need to use AG_EventLoop_FixedFPS(). Agar allows you to use a custom event loop, but in practice this is only useful in specialized applications such as embedded devices, or to overlay Agar GUI elements on top of an existing SDL or OpenGL application.

If you do need a custom event loop, here is a sample application which uses a minimal event loop equivalent to AG_EventLoop_FixedFPS(). As you can see, Agar only requires that you:

  1. Invoke AG_WidgetDraw() on every visible window (and naturally update the video region associated with the window).
  2. Relay events to AG_ProcessEvent(), so that Agar-GUI can respond to input device events (and SDL_VIDEORESIZE). The argument is of type SDL_Event, but events don't have to come from the SDL. For example, you could process events from a remote control using LIRC and pass a SDL_Event with type = SDL_KEYUP or SDL_KEYDOWN.
  3. Update Agar's wheel of time by calling AG_ProcessTimeout(). As an optimization, you only need to call the function if the list agTimeoutObjQ is non-empty.

Example 1-1: Basic event loop (source)

#include <agar/config/have_opengl.h>
 
#include <agar/core.h>
#include <agar/gui.h>
#include <agar/gui/opengl.h>
 
static void
MyEventLoop(void)
{
	SDL_Event ev;
	AG_Window *win;
	Uint32 Tr1 = SDL_GetTicks(), Tr2 = 0;
 
	for (;;) {
		Tr2 = SDL_GetTicks();
		if (Tr2-Tr1 >= agView->rNom) {		/* Time to redraw? */
			AG_LockVFS(agView);
#ifdef HAVE_OPENGL
			if (agView->opengl) {
				glClear(GL_COLOR_BUFFER_BIT|
				        GL_DEPTH_BUFFER_BIT);
			}
#endif
			/*
			 * Render the GUI windows and generate a list of
			 * dirty rectangles.
			 */
			AG_TAILQ_FOREACH(win, &agView->windows, windows) {
				AG_ObjectLock(win);
				if (!win->visible) {
					AG_ObjectUnlock(win);
					continue;
				}
				AG_WidgetDraw(win);
				if (!(win->flags & AG_WINDOW_NOUPDATERECT)) {
					AG_QueueVideoUpdate(
					    AGWIDGET(win)->x, AGWIDGET(win)->y,
					    AGWIDGET(win)->w, AGWIDGET(win)->h);
				}
				AG_ObjectUnlock(win);
			}
			/*
			 * Update the dirty rectangles. In OpenGL mode,
			 * we always update the entire display.
			 */
			if (agView->ndirty > 0) {
#ifdef HAVE_OPENGL
				if (agView->opengl) {
					SDL_GL_SwapBuffers();
				} else
#endif
				{
					SDL_UpdateRects(agView->v,
					    agView->ndirty,
					    agView->dirty);
				}
				agView->ndirty = 0;
			}
			AG_UnlockVFS(agView);
 
			/* Recalibrate the effective refresh rate. */
			Tr1 = SDL_GetTicks();
			agView->rCur = agView->rNom - (Tr1-Tr2);
			if (agView->rCur < 1) {
				agView->rCur = 1;
			}
		} else if (SDL_PollEvent(&ev) != 0) {
			/* Send all SDL events to Agar-GUI. */
			AG_ProcessEvent(&ev);
		} else if (AG_TAILQ_FIRST(&agTimeoutObjQ) != NULL) {
			/* Advance the timing wheels. */
			AG_ProcessTimeout(Tr2);
		} else if (agView->rCur > agIdleThresh) {
			/* Idle the rest of the time. */
			SDL_Delay(agView->rCur - agIdleThresh);
		}
	}
}
 
int
main(int argc, char *argv)
{
	AG_Window *win;
	AG_Table *tbl;
 
	if (AG_InitCore("event1", 0) == -1 ||
	    AG_InitVideo(320, 240, 32, AG_VIDEO_RESIZABLE) == -1) {
		return (1);
	}
	win = AG_WindowNew(0);
	AG_LabelNewStatic(win, 0, "Hello, world!");
	AG_WindowShow(win);
	MyEventLoop();
	return (0);
}
Notes on Agar-1.4

Agar-1.4 is going to implement support for multiple windows and will no longer depend on SDL by default. There will no longer be a single agView structure, so although we will do our best to provide backwards compatibility, users of custom event loops should expect that minor changes to their code will be needed.