This is an automated email from the ASF dual-hosted git repository.
GUIDINGLI pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new e871059fa games: support supervised loadable game processes
e871059fa is described below
commit e871059fa4f3f109903eb025db8eb45e4e059fac
Author: aviralgarg05 <[email protected]>
AuthorDate: Sat Aug 15 16:32:03 2026 +0530
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 <[email protected]>
---
games/cgol/Kconfig | 12 +++++-
games/cgol/cgol_main.c | 107 ++++++++++++++++++++++++++++++++++++++-----------
games/match4/Kconfig | 2 +-
games/snake/Kconfig | 2 +-
4 files changed, 95 insertions(+), 28 deletions(-)
diff --git a/games/cgol/Kconfig b/games/cgol/Kconfig
index d07a71251..3271b82de 100644
--- a/games/cgol/Kconfig
+++ b/games/cgol/Kconfig
@@ -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
diff --git a/games/cgol/cgol_main.c b/games/cgol/cgol_main.c
index 88e26d2f7..11e08515b 100644
--- a/games/cgol/cgol_main.c
+++ b/games/cgol/cgol_main.c
@@ -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;
}
diff --git a/games/match4/Kconfig b/games/match4/Kconfig
index cd916fd45..9cb1dd801 100644
--- a/games/match4/Kconfig
+++ b/games/match4/Kconfig
@@ -4,7 +4,7 @@
#
config GAMES_MATCH4
- bool "Match 4 Game"
+ tristate "Match 4 Game"
default n
---help---
Enable Match 4 game.
diff --git a/games/snake/Kconfig b/games/snake/Kconfig
index 35be60035..7f76396a7 100644
--- a/games/snake/Kconfig
+++ b/games/snake/Kconfig
@@ -4,7 +4,7 @@
#
config GAMES_SNAKE
- bool "Snake Game"
+ tristate "Snake Game"
default n
---help---
Enable Snake game.