mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-24 23:18:23 +00:00
games: support supervised loadable game processes
Allow cgol, match4, and snake to build as modules. Let cgol handle SIGTERM from its render loop and release the framebuffer. Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
This commit is contained in:
parent
31342e02ce
commit
e871059fa4
4 changed files with 95 additions and 28 deletions
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
|
||||
config GAMES_CGOL
|
||||
bool "Conway's Game of Life"
|
||||
tristate "Conway's Game of Life"
|
||||
depends on VIDEO_FB
|
||||
default n
|
||||
---help---
|
||||
|
|
@ -25,7 +25,7 @@ config GAMES_CGOL_PRIORITY
|
|||
|
||||
config GAMES_CGOL_STACKSIZE
|
||||
int "CGOL stack size"
|
||||
default DEFAULT_TASK_STACKSIZE
|
||||
default 4096
|
||||
|
||||
config GAMES_CGOL_FBDEV
|
||||
string "CGOL frame buffer device path"
|
||||
|
|
@ -34,6 +34,14 @@ config GAMES_CGOL_FBDEV
|
|||
The frame buffer device path that will be used by default for rendering.
|
||||
Device path can still be selected from first command line argument.
|
||||
|
||||
config GAMES_CGOL_YOFFSET
|
||||
int "Top framebuffer rows to reserve"
|
||||
range 0 500000
|
||||
default 0
|
||||
---help---
|
||||
Leave this many rows at the top of the framebuffer unchanged. This
|
||||
allows a launcher or supervisor to reserve a status or control bar.
|
||||
|
||||
config GAMES_CGOL_DBLBUF
|
||||
bool "CGOL double buffered"
|
||||
default n
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -174,6 +175,12 @@ struct fb_state_s
|
|||
struct fb_videoinfo_s vinfo;
|
||||
struct fb_planeinfo_s pinfo;
|
||||
unsigned int scale;
|
||||
|
||||
/* Rows reserved for the supervisor. */
|
||||
|
||||
unsigned int abs_yoff;
|
||||
unsigned int usable_yres;
|
||||
|
||||
int fd;
|
||||
void *fb; /* Real frame buffer */
|
||||
#ifdef CONFIG_GAMES_CGOL_DBLBUF
|
||||
|
|
@ -190,10 +197,20 @@ typedef void (*cell_render_f)(const struct fb_state_s *, uint32_t x,
|
|||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/* Polled by the render loop for cooperative shutdown. */
|
||||
|
||||
static volatile sig_atomic_t g_quit_requested;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void quit_signal_handler(int signo)
|
||||
{
|
||||
(void)signo;
|
||||
g_quit_requested = 1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: cgol_init
|
||||
*
|
||||
|
|
@ -515,28 +532,30 @@ static void cgol_advance(unsigned int *map)
|
|||
static void cgol_render_update(const struct fb_state_s *state)
|
||||
{
|
||||
#ifdef CONFIG_FB_UPDATE
|
||||
struct fb_area_s *full_screen;
|
||||
struct fb_area_s playfield;
|
||||
int err;
|
||||
|
||||
/* Create an area with the dimensions of the full screen for updating the
|
||||
* frame buffer after render operations are complete.
|
||||
*/
|
||||
/* Update only the game area. */
|
||||
|
||||
full_screen.x = 0;
|
||||
full_screen.y = 0;
|
||||
full_screen.w = fb_state.vinfo.xres;
|
||||
full_screen.h = fb_state.vinfo.yres;
|
||||
playfield.x = 0;
|
||||
playfield.y = state->abs_yoff;
|
||||
playfield.w = state->vinfo.xres;
|
||||
playfield.h = state->usable_yres;
|
||||
#endif
|
||||
|
||||
/* If double buffering, copy the RAM buffer to the frame buffer */
|
||||
/* Copy only the game area from the double buffer. */
|
||||
|
||||
#ifdef CONFIG_GAMES_CGOL_DBLBUF
|
||||
memcpy(state->fb, state->rambuf, state->pinfo.fblen);
|
||||
memcpy((FAR uint8_t *)state->fb + state->pinfo.stride * state->abs_yoff,
|
||||
(FAR uint8_t *)state->rambuf +
|
||||
state->pinfo.stride * state->abs_yoff,
|
||||
state->pinfo.stride * state->usable_yres);
|
||||
#endif
|
||||
|
||||
/* If the frame buffer on this device needs explicit updates, do that */
|
||||
|
||||
#ifdef CONFIG_FB_UPDATE
|
||||
err = ioctl(fb_state.fd, FBIO_UPDATE, (uintptr_t)&full_screen);
|
||||
err = ioctl(state->fd, FBIO_UPDATE, (uintptr_t)&playfield);
|
||||
if (err < 0)
|
||||
{
|
||||
fprintf(stderr, "Couldn't update screen: %d\n", errno);
|
||||
|
|
@ -567,9 +586,10 @@ static void cgol_render_clear(const struct fb_state_s *state)
|
|||
switch (state->pinfo.bpp)
|
||||
{
|
||||
case 32:
|
||||
for (uint32_t y = 0; y < state->pinfo.yres_virtual; y++)
|
||||
for (uint32_t y = 0; y < state->usable_yres; y++)
|
||||
{
|
||||
uint8_t *row = render_buf(state) + state->pinfo.stride * y;
|
||||
uint8_t *row = render_buf(state) +
|
||||
state->pinfo.stride * (y + state->abs_yoff);
|
||||
for (uint32_t x = 0; x < state->pinfo.xres_virtual; x++)
|
||||
{
|
||||
((uint32_t *)(row))[x] = BG32;
|
||||
|
|
@ -578,9 +598,10 @@ static void cgol_render_clear(const struct fb_state_s *state)
|
|||
break;
|
||||
|
||||
case 24:
|
||||
for (uint32_t y = 0; y < state->pinfo.yres_virtual; y++)
|
||||
for (uint32_t y = 0; y < state->usable_yres; y++)
|
||||
{
|
||||
uint8_t *row = render_buf(state) + state->pinfo.stride * y;
|
||||
uint8_t *row = render_buf(state) +
|
||||
state->pinfo.stride * (y + state->abs_yoff);
|
||||
for (uint32_t x = 0; x < state->pinfo.xres_virtual; x++)
|
||||
{
|
||||
*row++ = RGB24BLUE(BG24);
|
||||
|
|
@ -592,9 +613,10 @@ static void cgol_render_clear(const struct fb_state_s *state)
|
|||
|
||||
case 16:
|
||||
{
|
||||
for (uint32_t y = 0; y < state->pinfo.yres_virtual; y++)
|
||||
for (uint32_t y = 0; y < state->usable_yres; y++)
|
||||
{
|
||||
uint8_t *row = render_buf(state) + state->pinfo.stride * y;
|
||||
uint8_t *row = render_buf(state) +
|
||||
state->pinfo.stride * (y + state->abs_yoff);
|
||||
for (uint32_t x = 0; x < state->pinfo.xres_virtual; x++)
|
||||
{
|
||||
((uint16_t *)(row))[x] = BG16;
|
||||
|
|
@ -604,7 +626,12 @@ static void cgol_render_clear(const struct fb_state_s *state)
|
|||
break;
|
||||
|
||||
case 8:
|
||||
memset(render_buf(state), BG8, state->pinfo.fblen);
|
||||
for (uint32_t y = 0; y < state->usable_yres; y++)
|
||||
{
|
||||
uint8_t *row = render_buf(state) +
|
||||
state->pinfo.stride * (y + state->abs_yoff);
|
||||
memset(row, BG8, state->pinfo.xres_virtual);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -633,6 +660,8 @@ static void cgol_render_cell8(const struct fb_state_s *state, uint32_t x,
|
|||
x *= state->scale;
|
||||
y *= state->scale;
|
||||
|
||||
y += state->abs_yoff;
|
||||
|
||||
/* Starting at the (x, y) pair, we draw `scale` cells in each direction */
|
||||
|
||||
for (uint8_t yy = 0; yy < state->scale; yy++)
|
||||
|
|
@ -671,6 +700,8 @@ static void cgol_render_cell16(const struct fb_state_s *state, uint32_t x,
|
|||
x *= state->scale;
|
||||
y *= state->scale;
|
||||
|
||||
y += state->abs_yoff;
|
||||
|
||||
/* Starting at the (x, y) pair, we draw `scale` cells in each direction */
|
||||
|
||||
for (uint8_t yy = 0; yy < state->scale; yy++)
|
||||
|
|
@ -709,6 +740,8 @@ static void cgol_render_cell24(const struct fb_state_s *state, uint32_t x,
|
|||
x *= state->scale;
|
||||
y *= state->scale;
|
||||
|
||||
y += state->abs_yoff;
|
||||
|
||||
/* Starting at the (x, y) pair, we draw `scale` cells in each direction */
|
||||
|
||||
for (uint8_t yy = 0; yy < state->scale; yy++)
|
||||
|
|
@ -749,6 +782,8 @@ static void cgol_render_cell32(const struct fb_state_s *state, uint32_t x,
|
|||
x *= state->scale;
|
||||
y *= state->scale;
|
||||
|
||||
y += state->abs_yoff;
|
||||
|
||||
/* Starting at the (x, y) pair, we draw `scale` cells in each direction */
|
||||
|
||||
for (uint8_t yy = 0; yy < state->scale; yy++)
|
||||
|
|
@ -914,6 +949,17 @@ int main(int argc, FAR char *argv[])
|
|||
unsigned int map[WORD_COUNT];
|
||||
unsigned int yscale;
|
||||
unsigned int xscale;
|
||||
struct sigaction sa;
|
||||
|
||||
g_quit_requested = 0;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = quit_signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGTERM, &sa, NULL) < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to install SIGTERM handler: %d\n", errno);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
|
|
@ -949,16 +995,25 @@ int main(int argc, FAR char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Leave the configured supervisor rows untouched. */
|
||||
|
||||
fb_state.abs_yoff = CONFIG_GAMES_CGOL_YOFFSET;
|
||||
|
||||
/* If the frame buffer resolution is too small to support our game at its
|
||||
* lowest resolution (one pixel per cell), we can't play :(
|
||||
*/
|
||||
|
||||
if (fb_state.vinfo.xres < CONFIG_GAMES_CGOL_MAPWIDTH ||
|
||||
fb_state.vinfo.yres < CONFIG_GAMES_CGOL_MAPHEIGHT)
|
||||
fb_state.vinfo.yres <
|
||||
CONFIG_GAMES_CGOL_MAPHEIGHT + fb_state.abs_yoff ||
|
||||
fb_state.pinfo.xres_virtual < CONFIG_GAMES_CGOL_MAPWIDTH ||
|
||||
fb_state.pinfo.yres_virtual <
|
||||
CONFIG_GAMES_CGOL_MAPHEIGHT + fb_state.abs_yoff)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Needed at least %u x %u px resolution, but got %u x %u",
|
||||
CONFIG_GAMES_CGOL_MAPWIDTH, CONFIG_GAMES_CGOL_MAPHEIGHT,
|
||||
"Needed at least %u x %u px resolution, but got %u x %u\n",
|
||||
CONFIG_GAMES_CGOL_MAPWIDTH,
|
||||
CONFIG_GAMES_CGOL_MAPHEIGHT + fb_state.abs_yoff,
|
||||
fb_state.vinfo.xres, fb_state.vinfo.yres);
|
||||
close(fb_state.fd);
|
||||
return EXIT_FAILURE;
|
||||
|
|
@ -1001,6 +1056,7 @@ int main(int argc, FAR char *argv[])
|
|||
if (fb_state.rambuf == NULL)
|
||||
{
|
||||
fprintf(stderr, "Couldn't allocate double buffer: %d\n", errno);
|
||||
munmap(fb_state.fb, fb_state.pinfo.fblen);
|
||||
close(fb_state.fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -1012,8 +1068,10 @@ int main(int argc, FAR char *argv[])
|
|||
* This is selected by picking the minimum of the scale options.
|
||||
*/
|
||||
|
||||
fb_state.usable_yres = fb_state.pinfo.yres_virtual - fb_state.abs_yoff;
|
||||
|
||||
xscale = fb_state.pinfo.xres_virtual / CONFIG_GAMES_CGOL_MAPWIDTH;
|
||||
yscale = fb_state.pinfo.yres_virtual / CONFIG_GAMES_CGOL_MAPHEIGHT;
|
||||
yscale = fb_state.usable_yres / CONFIG_GAMES_CGOL_MAPHEIGHT;
|
||||
fb_state.scale = xscale < yscale ? xscale : yscale;
|
||||
|
||||
/* Now we can seed the game with some random starting cells */
|
||||
|
|
@ -1024,9 +1082,9 @@ int main(int argc, FAR char *argv[])
|
|||
|
||||
cgol_render_clear(&fb_state);
|
||||
|
||||
/* Loop the game forever */
|
||||
/* Loop the game until asked to quit */
|
||||
|
||||
for (; ; )
|
||||
while (!g_quit_requested)
|
||||
{
|
||||
/* Render the freshly calculated cells */
|
||||
|
||||
|
|
@ -1052,6 +1110,7 @@ int main(int argc, FAR char *argv[])
|
|||
#ifdef CONFIG_GAMES_CGOL_DBLBUF
|
||||
free(fb_state.rambuf);
|
||||
#endif
|
||||
munmap(fb_state.fb, fb_state.pinfo.fblen);
|
||||
close(fb_state.fd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
|
||||
config GAMES_MATCH4
|
||||
bool "Match 4 Game"
|
||||
tristate "Match 4 Game"
|
||||
default n
|
||||
---help---
|
||||
Enable Match 4 game.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
|
||||
config GAMES_SNAKE
|
||||
bool "Snake Game"
|
||||
tristate "Snake Game"
|
||||
default n
|
||||
---help---
|
||||
Enable Snake game.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue