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.

Reply via email to