diff --git a/games/cgol/Kconfig b/games/cgol/Kconfig index d07a7125163..3271b82ded6 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 88e26d2f728..11e08515b95 100644 --- a/games/cgol/cgol_main.c +++ b/games/cgol/cgol_main.c @@ -82,6 +82,7 @@ #include #include #include +#include #include #include #include @@ -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 cd916fd459e..9cb1dd801db 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 35be6003512..7f76396a7a6 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.