This is an automated email from the ASF dual-hosted git repository.

acassis 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 68aa7a838 games/brickmatch: add touchscreen input and module support
68aa7a838 is described below

commit 68aa7a838bfcbcc1eac16a34458279ba7b0f3ca2
Author: aviralgarg05 <[email protected]>
AuthorDate: Sat Aug 15 16:31:45 2026 +0530

    games/brickmatch: add touchscreen input and module support
    
    Route touch presses through BrickMatch's input layer and allow the game to 
build as a module.
    
    Signed-off-by: aviralgarg05 <[email protected]>
---
 games/brickmatch/Kconfig          |  36 +++++++-
 games/brickmatch/bm_input_touch.h | 173 ++++++++++++++++++++++++++++++++++++++
 games/brickmatch/bm_inputs.h      |   3 +
 games/brickmatch/bm_main.c        | 129 ++++++++++++++++++++++++----
 4 files changed, 324 insertions(+), 17 deletions(-)

diff --git a/games/brickmatch/Kconfig b/games/brickmatch/Kconfig
index 1567a4ebc..e51be23fc 100644
--- a/games/brickmatch/Kconfig
+++ b/games/brickmatch/Kconfig
@@ -4,7 +4,7 @@
 #
 
 config GAMES_BRICKMATCH
-       bool "Brickmatch Game"
+       tristate "Brickmatch Game"
        default n
        ---help---
                Enable Brickmatch games. Brickmatch is like a mix
@@ -88,6 +88,10 @@ config GAMES_BRICKMATCH_USE_GESTURE
 
 config GAMES_BRICKMATCH_USE_GPIO
        bool "GPIO pins as Input"
+
+config GAMES_BRICKMATCH_USE_TOUCHSCREEN
+       bool "Touchscreen swipe as input"
+       depends on INPUT_TOUCHSCREEN
 endchoice
 
 if GAMES_BRICKMATCH_USE_GPIO
@@ -118,4 +122,34 @@ config GAMES_BRICKMATCH_RIGHT_KEY_PATH
 
 endif #GAMES_BRICKMATCH_USE_GPIO
 
+if GAMES_BRICKMATCH_USE_TOUCHSCREEN
+
+config GAMES_BRICKMATCH_TOUCH_DEVPATH
+       string "Touchscreen device path"
+       default "/dev/input0"
+       ---help---
+               Path of the touchscreen device to read swipe gestures from.
+
+config GAMES_BRICKMATCH_TOUCH_THRESHOLD
+       int "Minimum swipe distance"
+       range 1 32767
+       default 24
+       ---help---
+               Minimum movement in calibrated touchscreen coordinate units 
before
+               a drag is reported as a direction.
+
+endif #GAMES_BRICKMATCH_USE_TOUCHSCREEN
+
+if !GAMES_BRICKMATCH_USE_LED_MATRIX
+
+config GAMES_BRICKMATCH_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.
+
+endif # !GAMES_BRICKMATCH_USE_LED_MATRIX
+
 endif
diff --git a/games/brickmatch/bm_input_touch.h 
b/games/brickmatch/bm_input_touch.h
new file mode 100644
index 000000000..21253fd2b
--- /dev/null
+++ b/games/brickmatch/bm_input_touch.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+ * apps/games/brickmatch/bm_input_touch.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_GAMES_BRICKMATCH_BM_INPUT_TOUCH_H
+#define __APPS_GAMES_BRICKMATCH_BM_INPUT_TOUCH_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <nuttx/input/touchscreen.h>
+
+#include "bm_inputs.h"
+
+/****************************************************************************
+ * Preprocessor Definitions
+ ****************************************************************************/
+
+/* Touch origin for the current swipe. */
+
+static bool g_bm_touch_active;
+static int32_t g_bm_touch_origin_x;
+static int32_t g_bm_touch_origin_y;
+
+/****************************************************************************
+ * Name: dev_input_init
+ *
+ * Description:
+ *   Initialize input method.
+ *
+ * Parameters:
+ *   dev - Input state data
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. A negated errno value is returned on
+ *   failure.
+ *
+ ****************************************************************************/
+
+int dev_input_init(FAR struct input_state_s *dev)
+{
+  dev->fd_touch = open(CONFIG_GAMES_BRICKMATCH_TOUCH_DEVPATH,
+                       O_RDONLY | O_NONBLOCK);
+  if (dev->fd_touch < 0)
+    {
+      int errcode = errno;
+      fprintf(stderr, "ERROR: Failed to open %s: %d\n",
+              CONFIG_GAMES_BRICKMATCH_TOUCH_DEVPATH, errcode);
+      return -errcode;
+    }
+
+  g_bm_touch_active = false;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: dev_read_input
+ *
+ * Description:
+ *   Convert a touchscreen swipe to a game direction.
+ *
+ * Parameters:
+ *   dev - Input state data
+ *
+ * Returned Value:
+ *   Zero (OK)
+ *
+ ****************************************************************************/
+
+int dev_read_input(FAR struct input_state_s *dev)
+{
+  struct touch_sample_s sample;
+  int32_t dx;
+  int32_t dy;
+  ssize_t nbytes;
+
+  dev->dir = DIR_NONE;
+
+  nbytes = read(dev->fd_touch, &sample, sizeof(sample));
+  if (nbytes < 0)
+    {
+      if (errno == EAGAIN || errno == EWOULDBLOCK)
+        {
+          usleep(10000);
+          return OK;
+        }
+
+      return -errno;
+    }
+
+  if (nbytes != sizeof(sample) || sample.npoints < 1)
+    {
+      return OK;
+    }
+
+  if (sample.point[0].flags & TOUCH_UP)
+    {
+      g_bm_touch_active = false;
+      return OK;
+    }
+
+  if ((sample.point[0].flags & TOUCH_POS_VALID) == 0)
+    {
+      return OK;
+    }
+
+  if (sample.point[0].flags & TOUCH_DOWN)
+    {
+      g_bm_touch_active = true;
+      g_bm_touch_origin_x = sample.point[0].x;
+      g_bm_touch_origin_y = sample.point[0].y;
+      return OK;
+    }
+
+  if (!g_bm_touch_active)
+    {
+      return OK;
+    }
+
+  dx = sample.point[0].x - g_bm_touch_origin_x;
+  dy = sample.point[0].y - g_bm_touch_origin_y;
+
+  if (abs(dx) < CONFIG_GAMES_BRICKMATCH_TOUCH_THRESHOLD &&
+      abs(dy) < CONFIG_GAMES_BRICKMATCH_TOUCH_THRESHOLD)
+    {
+      return OK;
+    }
+
+  if (abs(dx) > abs(dy))
+    {
+      dev->dir = dx > 0 ? DIR_RIGHT : DIR_LEFT;
+    }
+  else
+    {
+      dev->dir = dy > 0 ? DIR_DOWN : DIR_UP;
+    }
+
+  g_bm_touch_origin_x = sample.point[0].x;
+  g_bm_touch_origin_y = sample.point[0].y;
+
+  return OK;
+}
+
+#endif /* __APPS_GAMES_BRICKMATCH_BM_INPUT_TOUCH_H */
diff --git a/games/brickmatch/bm_inputs.h b/games/brickmatch/bm_inputs.h
index 9497b3135..f68c8047b 100644
--- a/games/brickmatch/bm_inputs.h
+++ b/games/brickmatch/bm_inputs.h
@@ -64,6 +64,9 @@ struct input_state_s
 #ifdef CONFIG_GAMES_BRICKMATCH_USE_GPIO
   int fd_gpio;
 #endif
+#ifdef CONFIG_GAMES_BRICKMATCH_USE_TOUCHSCREEN
+  int fd_touch;
+#endif
 
   int dir;      /* Direction to move the blocks */
 };
diff --git a/games/brickmatch/bm_main.c b/games/brickmatch/bm_main.c
index b9e63ca5d..0c5712c1d 100644
--- a/games/brickmatch/bm_main.c
+++ b/games/brickmatch/bm_main.c
@@ -28,6 +28,7 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -58,6 +59,10 @@
 #include "bm_input_gpio.h"
 #endif
 
+#ifdef CONFIG_GAMES_BRICKMATCH_USE_TOUCHSCREEN
+#include "bm_input_touch.h"
+#endif
+
 /****************************************************************************
  * Preprocessor Definitions
  ****************************************************************************/
@@ -93,6 +98,13 @@ struct screen_state_s
   struct fb_overlayinfo_s oinfo;
 #endif
   FAR void *fbmem;
+
+#ifndef CONFIG_GAMES_BRICKMATCH_USE_LED_MATRIX
+  /* Board offset within the framebuffer. */
+
+  int abs_xoff;
+  int abs_yoff;
+#endif
 };
 
 struct game_screen_s
@@ -112,6 +124,16 @@ struct game_screen_s
  * Private Data
  ****************************************************************************/
 
+/* Polled by the main loop for cooperative shutdown. */
+
+static volatile sig_atomic_t g_quit_requested;
+
+static void quit_signal_handler(int signo)
+{
+  (void)signo;
+  g_quit_requested = 1;
+}
+
 #ifndef CONFIG_GAMES_BRICKMATCH_USE_LED_MATRIX
 static const char g_default_fbdev[] = "/dev/fb0";
 #else
@@ -133,7 +155,7 @@ int prev_board[ROW][COL] =
 
 /* Colors used in the game plus Black */
 
-static const uint16_t pallete[NCOLORS + 1] =
+static const uint16_t palette[NCOLORS + 1] =
 {
   RGB16_BLACK,
   RGB16_BLUE,
@@ -173,13 +195,14 @@ static void draw_rect(FAR struct screen_state_s *state,
   int x;
   int y;
 
-  row = (FAR uint8_t *)state->fbmem + state->pinfo.stride * area->y;
+  row = (FAR uint8_t *)state->fbmem +
+        state->pinfo.stride * (area->y + state->abs_yoff);
   for (y = 0; y < area->h; y++)
     {
-      dest = ((FAR uint16_t *)row) + area->x;
+      dest = ((FAR uint16_t *)row) + area->x + state->abs_xoff;
       for (x = 0; x < area->w; x++)
         {
-          *dest++ = pallete[color];
+          *dest++ = palette[color];
         }
 
       row += state->pinfo.stride;
@@ -204,11 +227,17 @@ static void draw_rect(FAR struct screen_state_s *state,
 void update_screen(FAR struct screen_state_s *state,
                    FAR struct fb_area_s *area)
 {
+#ifdef CONFIG_FB_UPDATE
+  struct fb_area_s abs_area;
   int ret;
 
-#ifdef CONFIG_FB_UPDATE
+  abs_area.x = area->x + state->abs_xoff;
+  abs_area.y = area->y + state->abs_yoff;
+  abs_area.w = area->w;
+  abs_area.h = area->h;
+
   ret = ioctl(state->fd_fb, FBIO_UPDATE,
-              (unsigned long)((uintptr_t)area));
+              (unsigned long)((uintptr_t)&abs_area));
   if (ret < 0)
     {
       int errcode = errno;
@@ -410,7 +439,7 @@ void draw_board(FAR struct screen_state_s *state,
     {
       for (y = 1; y <= BOARDY_SIZE; y++)
         {
-          rgb_val = RGB16TO24(pallete[board[x][y]]);
+          rgb_val = RGB16TO24(palette[board[x][y]]);
           tmp = dim_color(rgb_val, 0.15);
           *bp++ = ws2812_gamma_correct(tmp);
         }
@@ -990,7 +1019,23 @@ int main(int argc, FAR char *argv[])
   struct input_state_s input;
   struct screen_state_s state;
   struct fb_area_s area;
+  struct sigaction sa;
   int ret;
+  int status = EXIT_SUCCESS;
+#ifndef CONFIG_GAMES_BRICKMATCH_USE_LED_MATRIX
+  int usable_yres;
+#endif
+
+  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, "ERROR: Failed to install SIGTERM handler: %d\n",
+              errno);
+      return EXIT_FAILURE;
+    }
 
   init_board();
 
@@ -1024,12 +1069,20 @@ int main(int argc, FAR char *argv[])
   printf("     yres: %u\n", state.vinfo.yres);
   printf("  nplanes: %u\n", state.vinfo.nplanes);
 
-  /* Setup the screen information */
+  /* Fit the square board below the reserved rows and center it. */
+
+  if (state.vinfo.yres <= CONFIG_GAMES_BRICKMATCH_YOFFSET)
+    {
+      fprintf(stderr, "ERROR: Framebuffer is shorter than the y offset\n");
+      close(state.fd_fb);
+      return EXIT_FAILURE;
+    }
+
+  usable_yres = state.vinfo.yres - CONFIG_GAMES_BRICKMATCH_YOFFSET;
+  screen.xres = state.vinfo.xres > usable_yres ?
+                usable_yres : state.vinfo.xres;
+  screen.yres = screen.xres;
 
-  screen.xres = state.vinfo.xres > state.vinfo.yres ?
-                state.vinfo.yres : state.vinfo.xres;
-  screen.yres = state.vinfo.yres > state.vinfo.xres ?
-                state.vinfo.xres : state.vinfo.yres;
   screen.xoff = (screen.xres % NCOLORS) / 2;
   screen.yoff = (screen.yres % NCOLORS) / 2;
   screen.steps = 2;
@@ -1037,6 +1090,9 @@ int main(int argc, FAR char *argv[])
   screen.blklen = (screen.xres / NCOLORS);
   screen.ncolors = NCOLORS;
 
+  state.abs_xoff = (state.vinfo.xres - screen.xres) / 2;
+  state.abs_yoff = CONFIG_GAMES_BRICKMATCH_YOFFSET;
+
   /* Get the display planeinfo */
 
   ret = ioctl(state.fd_fb, FBIOGET_PLANEINFO,
@@ -1057,6 +1113,14 @@ int main(int argc, FAR char *argv[])
   printf("  display: %u\n", state.pinfo.display);
   printf("      bpp: %u\n", state.pinfo.bpp);
 
+  if (state.pinfo.xres_virtual < state.abs_xoff + screen.xres ||
+      state.pinfo.yres_virtual < state.abs_yoff + screen.yres)
+    {
+      fprintf(stderr, "ERROR: Virtual framebuffer is too small\n");
+      close(state.fd_fb);
+      return EXIT_FAILURE;
+    }
+
   state.fbmem = mmap(NULL, state.pinfo.fblen, PROT_READ | PROT_WRITE,
                      MAP_SHARED | MAP_FILE, state.fd_fb, 0);
   if (state.fbmem == MAP_FAILED)
@@ -1088,15 +1152,37 @@ int main(int argc, FAR char *argv[])
 
   draw_board(&state, &area, &screen);
 
-  dev_input_init(&input);
+  ret = dev_input_init(&input);
+  if (ret < 0)
+    {
+      fprintf(stderr, "ERROR: Failed to initialize input: %d\n", ret);
+#ifndef CONFIG_GAMES_BRICKMATCH_USE_LED_MATRIX
+      munmap(state.fbmem, state.pinfo.fblen);
+#else
+      free(state.fbmem);
+#endif
+      close(state.fd_fb);
+      return EXIT_FAILURE;
+    }
 
   input.dir = DIR_NONE;
 
-  while (1)
+  while (!g_quit_requested)
     {
-      while (input.dir == DIR_NONE)
+      while (input.dir == DIR_NONE && !g_quit_requested)
         {
           ret = dev_read_input(&input);
+          if (ret < 0)
+            {
+              fprintf(stderr, "ERROR: Failed to read input: %d\n", ret);
+              status = EXIT_FAILURE;
+              g_quit_requested = 1;
+            }
+        }
+
+      if (g_quit_requested)
+        {
+          break;
         }
 
       screen.dir = input.dir;
@@ -1168,5 +1254,16 @@ int main(int argc, FAR char *argv[])
       fill_edge();
     }
 
-  return 0;
+#ifndef CONFIG_GAMES_BRICKMATCH_USE_LED_MATRIX
+  munmap(state.fbmem, state.pinfo.fblen);
+#else
+  free(state.fbmem);
+#endif
+
+#ifdef CONFIG_GAMES_BRICKMATCH_USE_TOUCHSCREEN
+  close(input.fd_touch);
+#endif
+  close(state.fd_fb);
+
+  return status;
 }

Reply via email to