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:
aviralgarg05 2026-08-15 16:32:03 +05:30 committed by GUIDINGLI
parent 31342e02ce
commit e871059fa4
4 changed files with 95 additions and 28 deletions

View file

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

View file

@ -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;
}

View file

@ -4,7 +4,7 @@
#
config GAMES_MATCH4
bool "Match 4 Game"
tristate "Match 4 Game"
default n
---help---
Enable Match 4 game.

View file

@ -4,7 +4,7 @@
#
config GAMES_SNAKE
bool "Snake Game"
tristate "Snake Game"
default n
---help---
Enable Snake game.