Remove conway, pong and snake games.

These games were poorly written and didn't work in graphics mode as they
required a vga text mode backend. It's less of a maintenance burden to just
remove them as that means the dispd vga text mode support can be deleted.
This commit is contained in:
Jonas 'Sortie' Termansen 2014-06-11 18:33:47 +02:00
parent 91a1df02b1
commit 0e577950c0
6 changed files with 3 additions and 764 deletions

View File

@ -38,8 +38,7 @@ limited but works on most systems.
You can change the display resolution using the `chvideomode` program. However,
you need a suitable kernel driver to do the mode setting. The current release
only has a simple BGA driver, which works in virtual machines such as Qemu,
VirtualBox or Bochs. Note that some programs does not work outside of VGA Text
Mode because the dispd library doesn't yet have a VGA->framebuffer converter.
VirtualBox or Bochs.
The BGA driver can support virtually any resolution, but since the interface
fails to report which resolutions are appropriate in the current context, I have
@ -152,7 +151,6 @@ Sortix comes with a number of home-made programs. Here is an overview:
* `clear` - clear terminal
* `colormake` - colorful version of make (if make is available)
* `column` - format lines nicely in columns
* `conway` - game of life simulator
* `cp` - copy file
* `date` - display current time and date
* `dirname` - strip last component from file name
@ -180,14 +178,12 @@ Sortix comes with a number of home-made programs. Here is an overview:
* `mkinitrd` - create an initrd
* `mv` - move a file
* `pager` - display file page by page
* `pong` - remake of the classic pong game
* `pwd` - print current directory path
* `regress` - run system tests
* `rm` - remove file
* `rmdir` - remove empty directory
* `sh` - alias for the shell
* `sleep` - delay for a specified amount of time
* `snake` - remake of the classic snake game
* `sort` - sort lines of text files
* `tail` - display end of file
* `time` - measure program running time
@ -278,26 +274,6 @@ Included Games
The system comes with a number of small casual games. Note that some games
require running in the correct display mode before they will function.
### Pong ###
The program `pong` implements a simple pong clone that uses the VGA text mode
buffer. Player one controls using `W` and `S`, while player two controls using
the arrow keys. This program is only available in VGA mode.
### Snake ###
This is a simple snake clone that only works in VGA text mode. You control the
snake using the `W`, `S`, `A`, and `D` keys. To run, start the program `snake`.
This program is only available in VGA mode.
### Conway's Game of Life ###
You can do turing-complete calculations in the classic Game of Life in the VGA
text mode buffer. Simply start the program `conway` and use `W`, `A`, `S`, and
`D` to navigate the screen. `Space` toggles the current point between live and
dead. `C` clears the entire screen, and `R` starts and stops the simulation.
This program is only available in VGA mode.
### Asteroids ###
Mine for crystals in an asteroid field! Start the `asteroids` program and if you
@ -481,8 +457,8 @@ likely want to recompile large parts of user-land if you update this library.
The dispd library allows processes to communicate with the dispd server that
handles window management and graphical output. Currently, there is no such
server and the library calls the kernel interface itself. This library allows
games such as asteroids and pong to detect the current resolution and request
whatever resolution they need to function.
games such as asteroids to detect the current resolution and request whatever
resolution they need to function.
cd /src/dispd
make

3
games/.gitignore vendored
View File

@ -4,7 +4,4 @@
*.iso
*.so
*.a
pong
conway
snake
asteroids

View File

@ -9,9 +9,6 @@ CXXFLAGS?=$(OPTLEVEL)
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
BINARIES:=\
pong \
conway \
snake \
asteroids \
LIBS:=-ldispd

View File

@ -1,210 +0,0 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
conway.cpp
Conway's Game of Life.
*******************************************************************************/
#include <sortix/vga.h>
#include <sys/keycodes.h>
#include <sys/termmode.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
#include <string.h>
#include <dispd.h>
const int width = 80;
const int height = 25;
const int rowstride = width + 2;
const int buffersize = (height+2) * (width+2);
uint16_t frame[width*height];
bool running;
int posx;
int posy;
char framea[buffersize];
char frameb[buffersize];
char* currentframe;
char* lastframe;
void Clear()
{
// Reset the game data.
for ( int i = 0; i < buffersize; i++ ) { framea[i] = 0; frameb[i] = 0; }
}
int Init()
{
Clear();
// Initialize variables.
currentframe = framea;
lastframe = frameb;
running = false;
posy = height / 2 + 1;
posx = width / 2 + 1;
return 0;
}
void Cycle()
{
// Run a cycle of the game of life.
for ( int y = 1; y <= height; y++ )
{
for ( int x = 1; x <= width; x++ )
{
int alivearound = 0;
int current = lastframe[y * rowstride + x];
if ( lastframe[(y-1) * rowstride + (x-1)] > 0 ) { alivearound++; }
if ( lastframe[(y-1) * rowstride + (x-0)] > 0 ) { alivearound++; }
if ( lastframe[(y-1) * rowstride + (x+1)] > 0 ) { alivearound++; }
if ( lastframe[(y-0) * rowstride + (x-1)] > 0 ) { alivearound++; }
if ( lastframe[(y-0) * rowstride + (x+1)] > 0 ) { alivearound++; }
if ( lastframe[(y+1) * rowstride + (x-1)] > 0 ) { alivearound++; }
if ( lastframe[(y+1) * rowstride + (x-0)] > 0 ) { alivearound++; }
if ( lastframe[(y+1) * rowstride + (x+1)] > 0 ) { alivearound++; }
currentframe[y * rowstride + x] = ( (current > 0 && (alivearound == 3 || alivearound == 2)) || (current == 0 && alivearound == 3) ) ? 1 : 0;
}
}
// Swap buffers.
char* tmp = lastframe; lastframe = currentframe; currentframe = tmp;
}
void Render()
{
uint16_t* dest = frame;
uint16_t set = 'O' | (COLOR8_LIGHT_GREY << 8);
uint16_t unset = ' ' | (COLOR8_LIGHT_GREY << 8);
// Render each cell directly to the VGA framebuffer!
for ( int y = 1; y <= height; y++ )
{
for ( int x = 1; x <= width; x++ )
{
dest[(y-1) * width + (x-1)] = (lastframe[y * rowstride + x] > 0) ? set : unset;
// Render the cursor.
if ( !running && x == posx && y == posy )
{
dest[(y-1) * width + (x-1)] |= (COLOR8_RED << 12);
}
}
}
}
void Update()
{
// Read the keyboard input from the user.
unsigned termmode = TERMMODE_KBKEY
| TERMMODE_UNICODE
| TERMMODE_SIGNAL
| TERMMODE_NONBLOCK;
if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); }
while ( true )
{
uint32_t codepoint;
ssize_t numbytes = read(0, &codepoint, sizeof(codepoint));
if ( !numbytes ) { break; }
if ( numbytes < 0 ) { break; }
int kbkey = KBKEY_DECODE(codepoint);
if ( kbkey == KBKEY_R ) { running = !running; }
if ( running ) { continue; }
if ( kbkey == KBKEY_C ) { Clear(); }
if ( kbkey == KBKEY_W ) { if ( posy > 1 ) { posy--; } }
if ( kbkey == KBKEY_A ) { if ( posx > 1 ) { posx--; } }
if ( kbkey == KBKEY_S ) { if ( posy < height ) { posy++; } }
if ( kbkey == KBKEY_D ) { if ( posx < width ) { posx++; } }
if ( kbkey == KBKEY_SPACE )
{
lastframe[posy * rowstride + posx] = 1 - lastframe[posy * rowstride + posx];
}
}
// Run a cycle.
if ( running ) { Cycle(); }
Render();
}
int usage(int /*argc*/, char* argv[])
{
printf("usage: %s [OPTIONS]\n", argv[0]);
printf("Options:\n");
printf(" --speed <miliseconds> How many miliseconds between updates\n");
printf(" --usage Display this screen\n");
printf(" --help Display this screen\n");
return 0;
}
int main(int argc, char* argv[])
{
if ( !dispd_initialize(&argc, &argv) )
error(1, 0, "couldn't initialize dispd library");
int sleepms = 50;
for ( int i = 1; i < argc; i++ )
{
if ( strcmp(argv[i], "--help") == 0 ) { return usage(argc, argv); }
if ( strcmp(argv[i], "--usage") == 0 ) { return usage(argc, argv); }
if ( strcmp(argv[i], "--speed") == 0 && 1 < argc-i )
{
sleepms = atoi(argv[++i]);
}
}
int result = Init();
if ( result != 0 ) { return result; }
struct dispd_session* session = dispd_attach_default_session();
if ( !session )
error(1, 0, "couldn't attach to dispd default session");
if ( !dispd_session_setup_game_vga(session) )
error(1, 0, "couldn't setup dispd vga session");
struct dispd_window* window = dispd_create_window_game_vga(session);
if ( !window )
error(1, 0, "couldn't create dispd vga window");
// Update the game every 50th milisecond.
while ( true )
{
usleep(sleepms * 1000);
Update();
struct dispd_framebuffer* fb = dispd_begin_render(window);
if ( !fb )
error(1, 0, "unable to begin rendering dispd window");
memcpy(dispd_get_framebuffer_data(fb), frame, sizeof(frame));
dispd_finish_render(fb);
}
return 0;
}

View File

@ -1,287 +0,0 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
pong.cpp
Two-player pong.
*******************************************************************************/
#include <sortix/vga.h>
#include <sys/keycodes.h>
#include <sys/termmode.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
#include <string.h>
#include <dispd.h>
int Init();
void Reset();
void ClearScreen();
void Collision();
void Goal(unsigned player);
void UpdateUI();
void Update();
void ReadInput();
int main(int argc, char* argv[]);
const int width = 80;
const int height = 25;
const int padsize = 5;
const unsigned goalfreq = 800;
const unsigned collisionfreq = 1200;
uint16_t frame[width*height];
int ballx;
int bally;
int oldballx;
int oldbally;
int ballvelx;
int ballvely;
int p1y;
int p2y;
bool p1vup;
bool p1vdown;
bool p2vup;
bool p2vdown;
unsigned p1score;
unsigned p2score;
unsigned time;
unsigned soundleft;
int Init()
{
Reset();
return 0;
}
void Reset()
{
ClearScreen();
ballx = width/2;
bally = height/2;
ballvelx = -1;
ballvely = -1;
oldballx = ballx - ballvelx;
oldbally = bally - ballvely;
p1y = (height-padsize) / 5;
p2y = (height-padsize) / 5;
p1vup = false;
p1vdown = false;
p2vup = false;
p2vdown = false;
p1score = 0;
p2score = 0;
time = 0;
soundleft = -1;
}
void ClearScreen()
{
for ( int y = 0; y < height; y++ )
{
uint16_t color = COLOR8_BLACK << 8;
for ( int x = 0; x < width; x++ )
{
frame[x + y*width] = ' ' | color;
}
}
}
void Collision()
{
//System::Sound::SetFrequency(collisionfreq);
soundleft = 40;
}
void Goal(unsigned player)
{
frame[ballx + bally*width] = ' ' | (COLOR8_WHITE << 8);
int offset = (rand() % 4) - 2;
ballx = width/2;
bally = height/2 + offset;
if ( player == 1 )
{
ballvelx = 1;
ballvely = (rand() % 2) * 2 - 1;
p1score++;
}
else if ( player == 2 )
{
ballvelx = -1;
ballvely = (rand() % 2) * 2 - 1;
p2score++;
}
if ( ballvely > 0 ) { ballvely /= ballvely; }
if ( ballvely < 0 ) { ballvely /= -ballvely; }
//System::Sound::SetFrequency(goalfreq);
soundleft = 50;
UpdateUI();
}
void UpdateUI()
{
for ( int x = 0; x < width; x++ ) { frame[x] = ' ' | (COLOR8_LIGHT_GREY << 12) | (COLOR8_RED << 8); }
char num[12];
int len;
len = sprintf(num, "%u", p1score);
for ( int i = 0; i < len; i++ ) { frame[i] = ( frame[i] & 0xFF00 ) | num[i]; }
len = sprintf(num, "%u", p2score);
for ( int i = 0; i < len; i++ ) { frame[width - len + i] = ( frame[width - len + i] & 0xFF00 ) | num[i]; }
}
void Update()
{
int p1v = 0, p2v = 0;
if ( p1vup && !p1vdown ) { p1v = -1; } else if ( !p1vup && p1vdown ) { p1v = 1; }
if ( p2vup && !p2vdown ) { p2v = -1; } else if ( !p2vup && p2vdown ) { p2v = 1; }
if ( p1v < 0 && p1y > 1 ) { p1y--; }
if ( p1v > 0 && p1y + padsize < height ) { p1y++; }
if ( p2v < 0 && p2y > 1 ) { p2y--; }
if ( p2v > 0 && p2y + padsize < height ) { p2y++; }
for ( int y = 1; y < height; y++ )
{
uint16_t color = ( y < p1y || y >= p1y + padsize ) ? COLOR8_BLACK << 12 : COLOR8_RED << 12; frame[y*width] = ' ' | color;
}
for ( int y = 1; y < height; y++ )
{
uint16_t color = ( y < p2y || y >= p2y + padsize ) ? COLOR8_BLACK << 12 : COLOR8_BLUE << 12; frame[width-1 + y*width] = ' ' | color;
}
if ( bally + ballvely <= 1 ) { ballvely = 0 - ballvely; Collision(); }
if ( bally + ballvely >= height ) { ballvely = 0 - ballvely; Collision(); }
if ( ballx + ballvelx < 1 ) { if ( bally + ballvely < p1y - 1 || bally + ballvely > p1y + padsize + 1 ) { Goal(2); } else { ballvelx = 0 - ballvelx; Collision(); } }
if ( ballx + ballvelx >= width-1 ) { if ( bally + ballvely < p2y - 1 || bally + ballvely > p2y + padsize + 1 ) { Goal(1); } else { ballvelx = 0 - ballvelx; Collision(); } }
frame[oldballx + oldbally*width] = ' ' | (COLOR8_WHITE << 8);
frame[ballx + bally*width] = '.' | (COLOR8_WHITE << 8);
oldballx = ballx; oldbally = bally;
ballx += ballvelx;
bally += ballvely;
frame[ballx + bally*width] = 'o' | (COLOR8_WHITE << 8);
}
void ReadInput()
{
unsigned termmode = TERMMODE_KBKEY
| TERMMODE_UNICODE
| TERMMODE_SIGNAL
| TERMMODE_NONBLOCK;
if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); }
while ( true )
{
uint32_t codepoint;
ssize_t numbytes = read(0, &codepoint, sizeof(codepoint));
if ( !numbytes ) { return; }
if ( numbytes < 0 ) { return; }
int kbkey = KBKEY_DECODE(codepoint);
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
if ( kbkey == KBKEY_ENTER ) { Reset(); }
if ( abskbkey == KBKEY_W ) { p1vup = (0 < kbkey); }
if ( abskbkey == KBKEY_S ) { p1vdown = (0 < kbkey); }
if ( abskbkey == KBKEY_UP ) { p2vup = (0 < kbkey); }
if ( abskbkey == KBKEY_DOWN ) { p2vdown = (0 < kbkey); }
}
}
int usage(int /*argc*/, char* argv[])
{
printf("usage: %s [OPTIONS]\n", argv[0]);
printf("Options:\n");
printf(" --speed <miliseconds> How many miliseconds between updates\n");
printf(" --usage Display this screen\n");
printf(" --help Display this screen\n");
return 0;
}
int main(int argc, char* argv[])
{
if ( !dispd_initialize(&argc, &argv) )
error(1, 0, "couldn't initialize dispd library");
int sleepms = 50;
for ( int i = 1; i < argc; i++ )
{
if ( strcmp(argv[i], "--help") == 0 ) { return usage(argc, argv); }
if ( strcmp(argv[i], "--usage") == 0 ) { return usage(argc, argv); }
if ( strcmp(argv[i], "--speed") == 0 && 1 < argc-i )
{
sleepms = atoi(argv[++i]);
}
}
int result = Init();
if ( result != 0 ) { return result; }
struct dispd_session* session = dispd_attach_default_session();
if ( !session )
error(1, 0, "couldn't attach to dispd default session");
if ( !dispd_session_setup_game_vga(session) )
error(1, 0, "couldn't setup dispd vga session");
struct dispd_window* window = dispd_create_window_game_vga(session);
if ( !window )
error(1, 0, "couldn't create dispd vga window");
while (true)
{
usleep(sleepms * 1000);
ReadInput();
Update();
UpdateUI();
struct dispd_framebuffer* fb = dispd_begin_render(window);
if ( !fb )
error(1, 0, "unable to begin rendering dispd window");
memcpy(dispd_get_framebuffer_data(fb), frame, sizeof(frame));
dispd_finish_render(fb);
if ( /*soundleft < 0*/ false ) { continue; }
if ( soundleft <= 50 )
{
//System::Sound::SetFrequency(0);
}
else
{
soundleft -= sleepms;
}
}
return 0;
}

View File

@ -1,234 +0,0 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
snake.cpp
Single-player snake.
*******************************************************************************/
#include <sortix/vga.h>
#include <sys/keycodes.h>
#include <sys/termmode.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
#include <string.h>
#include <dispd.h>
const int width = 80;
const int height = 25;
const int buffersize = height * width;
uint16_t frame[width*height];
int posx;
int posy;
int velx;
int vely;
int tailx;
int taily;
int taillen;
int tailmax;
int animalx;
int animaly;
char direction[buffersize];
uint16_t animal = '%' | (COLOR8_RED<<8);
uint16_t snake = ' ' | (COLOR8_GREEN<<12);
const int defaultspeed = 75;
const int speedincrease = -5;
const int maxspeed = 40;
volatile int speed;
bool tabhack = false;
void Clear()
{
// Reset the game data.
for ( int i = 0; i < buffersize; i++ ) { frame[i] = ' '; direction[i] = -1; }
}
void Reset()
{
Clear();
tailx = posx = width/2;
taily = posy = height/2;
switch ( rand() % 4 )
{
case 0: velx = -1; vely = 0; break;
case 1: velx = 1; vely = 0; break;
case 2: velx = 0; vely = -1; break;
case 3: velx = 0; vely = -1; break;
}
animalx = 2 + (rand() % (width-4));
animaly = 2 + (rand() % (height-4));
taillen = 0;
tailmax = 3;
frame[animaly * width + animalx] = animal;
speed = defaultspeed;
}
int Init()
{
Reset();
return 0;
}
bool tabhacking = false;
void Update()
{
int newvelx = velx;
int newvely = vely;
// Read the keyboard input from the user.
unsigned termmode = TERMMODE_KBKEY
| TERMMODE_UNICODE
| TERMMODE_SIGNAL
| TERMMODE_NONBLOCK;
if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); }
while ( true )
{
uint32_t codepoint;
ssize_t numbytes = read(0, &codepoint, sizeof(codepoint));
if ( !numbytes ) { break; }
if ( numbytes < 0 ) { break; }
int kbkey = KBKEY_DECODE(codepoint);
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
if ( tabhack && abskbkey == KBKEY_TAB ) { tabhacking = (0 < kbkey); }
if ( kbkey == KBKEY_ENTER ) { Reset(); return; }
if ( kbkey == KBKEY_W ) { newvelx = 0; newvely = -1; }
if ( kbkey == KBKEY_A ) { newvelx = -1; newvely = 0; }
if ( kbkey == KBKEY_S ) { newvelx = 0; newvely = 1; }
if ( kbkey == KBKEY_D ) { newvelx = 1; newvely = 0; }
}
if ( tabhack && tabhacking )
{
if ( animalx == posx && posy < animaly ) { newvelx = 0; newvely = 1; }
if ( animalx == posx && posy > animaly ) { newvelx = 0; newvely = -1; }
if ( animaly == posy && posx < animalx ) { newvelx = 1; newvely = 0; }
if ( animaly == posy && posx > animalx ) { newvelx = -1; newvely = 0; }
}
// Make sure we don't do a 180.
if ( !(velx == -1 && newvelx == 1) &&
!(velx == 1 && newvelx == -1) &&
!(vely == -1 && newvely == 1) &&
!(vely == 1 && newvely == -1) )
{
velx = newvelx; vely = newvely;
}
// Don't collide with the border!
if ( posx + velx < 0 || width <= posx + velx ||
posy + vely < 0 || height <= posy + vely ) { Reset(); return; }
int newx = posx + velx;
int newy = posy + vely;
// Move the tail, if needed.
if ( taillen == tailmax )
{
frame[taily * width + tailx] = ' '; taillen--;
switch ( direction[taily * width + tailx] )
{
case 0: tailx--; break;
case 1: tailx++; break;
case 2: taily--; break;
case 3: taily++; break;
}
}
// Check for collision.
if ( frame[newy * width + newx] == snake ) { Reset(); return; }
// Check for food.
if ( newx == animalx && newy == animaly )
{
tailmax++;
animalx = 2 + (rand() % (width-4));
animaly = 2 + (rand() % (height-4));
assert(0 <= animalx && animalx < width);
assert(0 <= animaly && animaly < height);
if ( maxspeed < speed ) { speed += speedincrease; }
}
frame[animaly * width + animalx] = animal;
// Remember where we are going.
int dir = 0;
if ( velx < 0 ) { dir = 0; }
if ( velx > 0 ) { dir = 1; }
if ( vely < 0 ) { dir = 2; }
if ( vely > 0 ) { dir = 3; }
direction[posy * width + posx] = dir;
// Move the head.
posx = newx;
posy = newy;
frame[posy * width + posx] = snake; taillen++;
}
int main(int argc, char* argv[])
{
if ( !dispd_initialize(&argc, &argv) )
error(1, 0, "couldn't initialize dispd library");
for ( int i = 1; i < argc; i++ )
{
if ( strcmp("--tabhack", argv[i]) == 0 ) { tabhack = true; }
}
int result = Init();
if ( result != 0 ) { return result; }
struct dispd_session* session = dispd_attach_default_session();
if ( !session )
error(1, 0, "couldn't attach to dispd default session");
if ( !dispd_session_setup_game_vga(session) )
error(1, 0, "couldn't setup dispd vga session");
struct dispd_window* window = dispd_create_window_game_vga(session);
if ( !window )
error(1, 0, "couldn't create dispd vga window");
// Update the game every once in a while.
while ( true )
{
usleep(speed * 1000);
Update();
struct dispd_framebuffer* fb = dispd_begin_render(window);
if ( !fb )
error(1, 0, "unable to begin rendering dispd window");
memcpy(dispd_get_framebuffer_data(fb), frame, sizeof(frame));
dispd_finish_render(fb);
}
return 0;
}