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