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.git

commit 17025085eadf33906ccb57ccbc95ba8b2c05b4ab
Author: Alan Carvalho de Assis <[email protected]>
AuthorDate: Wed Aug 12 10:44:07 2026 -0300

    arch/sim: Add support to emulated Mouse
    
    This commit adds support for three buttons Mouse emulation on SIM.
    
    Signed-off-by: Alan C. Assis <[email protected]>
    Assisted-by: Claude Code
---
 arch/sim/Kconfig                          |  15 +++
 arch/sim/src/Makefile                     |   3 +
 arch/sim/src/sim/CMakeLists.txt           |   3 +
 arch/sim/src/sim/posix/sim_x11eventloop.c |  98 ++++++++++++++
 arch/sim/src/sim/sim_initialize.c         |   6 +-
 arch/sim/src/sim/sim_internal.h           |  11 +-
 arch/sim/src/sim/sim_mouse.c              | 206 ++++++++++++++++++++++++++++++
 7 files changed, 338 insertions(+), 4 deletions(-)

diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig
index b1e78e1b60e..70b1f3c88ab 100644
--- a/arch/sim/Kconfig
+++ b/arch/sim/Kconfig
@@ -570,6 +570,21 @@ config SIM_TOUCHSCREEN
        ---help---
                Support an X11 mouse-based touchscreen emulation.  Also needs 
INPUT=y
 
+config SIM_MOUSE
+       bool "X11 Mouse Emulation"
+       select INPUT_MOUSE
+       depends on SIM_X11FB
+       ---help---
+               Support a full X11 mouse emulation, reporting left/right/middle
+               button identity and the scroll wheel via /dev/mouseN (struct
+               mouse_report_s). Unlike SIM_TOUCHSCREEN, which folds every X11
+               mouse button into one undifferentiated "pen down" contact
+               (matching real touchscreen hardware, which has no concept of a
+               second or third button), this preserves which physical button
+               was pressed -- needed by any GUI that distinguishes a 
right-click
+               context menu or middle-click from an ordinary left click.
+               Also needs INPUT=y
+
 config SIM_AJOYSTICK
        bool "X11 mouse-based analog joystick emulation"
        depends on SIM_X11FB
diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile
index cc6ad23c184..9a567e5b6c3 100644
--- a/arch/sim/src/Makefile
+++ b/arch/sim/src/Makefile
@@ -208,6 +208,9 @@ endif
 ifeq ($(CONFIG_SIM_TOUCHSCREEN),y)
   CSRCS += sim_touchscreen.c
   HOSTSRCS += sim_x11eventloop.c
+else ifeq ($(CONFIG_SIM_MOUSE),y)
+  CSRCS += sim_mouse.c
+  HOSTSRCS += sim_x11eventloop.c
 else ifeq ($(CONFIG_SIM_AJOYSTICK),y)
   CSRCS += sim_ajoystick.c
   HOSTSRCS += sim_x11eventloop.c
diff --git a/arch/sim/src/sim/CMakeLists.txt b/arch/sim/src/sim/CMakeLists.txt
index cc5f4845e4e..529c978269a 100644
--- a/arch/sim/src/sim/CMakeLists.txt
+++ b/arch/sim/src/sim/CMakeLists.txt
@@ -220,6 +220,9 @@ if(CONFIG_SIM_X11FB)
   if(CONFIG_SIM_TOUCHSCREEN)
     list(APPEND SRCS sim_touchscreen.c)
     list(APPEND HOSTSRCS sim_x11eventloop.c)
+  elseif(CONFIG_SIM_MOUSE)
+    list(APPEND SRCS sim_mouse.c)
+    list(APPEND HOSTSRCS sim_x11eventloop.c)
   elseif(CONFIG_SIM_AJOYSTICK)
     list(APPEND SRCS sim_ajoystick.c)
     list(APPEND HOSTSRCS sim_x11eventloop.c)
diff --git a/arch/sim/src/sim/posix/sim_x11eventloop.c 
b/arch/sim/src/sim/posix/sim_x11eventloop.c
index 15d8cd0ddcf..94553a42a61 100644
--- a/arch/sim/src/sim/posix/sim_x11eventloop.c
+++ b/arch/sim/src/sim/posix/sim_x11eventloop.c
@@ -91,6 +91,64 @@ static int sim_buttonmap(int state, int button)
 }
 #endif
 
+/****************************************************************************
+ * Name: sim_mousebuttonmap
+ *
+ * Description:
+ *   Same idea as sim_buttonmap() above, but producing a bitmap in
+ *   struct mouse_report_s's MOUSE_BUTTON_1/2/3 numbering (see
+ *   include/nuttx/input/mouse.h) instead of touch_sample_s's single
+ *   undifferentiated "pen down" bit -- the whole point of
+ *   CONFIG_SIM_MOUSE over CONFIG_SIM_TOUCHSCREEN is to keep which
+ *   physical button was pressed. Not including that header here since
+ *   it's the only thing that would pull NuttX input headers into this
+ *   host-compiled (HOSTSRCS) file; the three bit values are hand-rolled
+ *   instead, matching MOUSE_BUTTON_1=1, MOUSE_BUTTON_2=2, MOUSE_BUTTON_3=4.
+ *
+ *   Note X11's own button numbering has Button2 as the *middle* button
+ *   and Button3 as the *right* button (Button1 is left, same as
+ *   everywhere else) -- MOUSE_BUTTON_2/3 need the opposite assignment
+ *   (right, then middle) to match, so this cannot just reuse
+ *   sim_buttonmap()'s bit positions.
+ ****************************************************************************/
+
+#ifdef CONFIG_SIM_MOUSE
+static int sim_mousebuttonmap(int state, int button)
+{
+  int buttons = 0;
+
+  if ((state & Button1Mask) != 0)
+    {
+      buttons |= 1;   /* MOUSE_BUTTON_1: left */
+    }
+
+  if ((state & Button2Mask) != 0)
+    {
+      buttons |= 4;   /* MOUSE_BUTTON_3: middle */
+    }
+
+  if ((state & Button3Mask) != 0)
+    {
+      buttons |= 2;   /* MOUSE_BUTTON_2: right */
+    }
+
+  switch (button)
+    {
+      case Button1:
+        buttons ^= 1;
+        break;
+      case Button2:
+        buttons ^= 4;
+        break;
+      case Button3:
+        buttons ^= 2;
+        break;
+    }
+
+  return buttons;
+}
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -148,6 +206,46 @@ void sim_x11events(void)
             break;
           #endif
 
+          #ifdef CONFIG_SIM_MOUSE
+          case MotionNotify : /* Enabled by PointerMotionMask */
+            {
+              sim_mouseevent(event.xmotion.x, event.xmotion.y,
+                             sim_mousebuttonmap(event.xmotion.state, 0), 0);
+            }
+            break;
+
+          case ButtonPress  : /* Enabled by ButtonPressMask */
+          case ButtonRelease: /* Enabled by ButtonReleaseMask */
+            {
+              /* Buttons 4/5 are the traditional X11 scroll-wheel
+               * encoding: a Press/Release pair fires on every "click"
+               * of the wheel, with no held state of its own -- report
+               * the wheel delta on Press only (matching how a real
+               * mouse_report_s sample is consumed once, not polled as
+               * persistent state), and pass through the *other*
+               * buttons' current state unchanged either way.
+               */
+
+              int wheel = 0;
+
+              if (event.type == ButtonPress && event.xbutton.button == 4)
+                {
+                  wheel = 1;
+                }
+              else if (event.type == ButtonPress &&
+                       event.xbutton.button == 5)
+                {
+                  wheel = -1;
+                }
+
+              sim_mouseevent(event.xbutton.x, event.xbutton.y,
+                             sim_mousebuttonmap(event.xbutton.state,
+                                                event.xbutton.button),
+                             wheel);
+            }
+            break;
+          #endif
+
           default:
             break;
         }
diff --git a/arch/sim/src/sim/sim_initialize.c 
b/arch/sim/src/sim/sim_initialize.c
index 94a7aedc794..4b5d7d6cfa9 100644
--- a/arch/sim/src/sim/sim_initialize.c
+++ b/arch/sim/src/sim/sim_initialize.c
@@ -51,7 +51,7 @@
  ****************************************************************************/
 
 #if defined(CONFIG_SIM_TOUCHSCREEN) || defined(CONFIG_SIM_AJOYSTICK) || \
-    defined(CONFIG_SIM_BUTTONS)
+    defined(CONFIG_SIM_BUTTONS) || defined(CONFIG_SIM_MOUSE)
 static struct work_s g_x11event_work;   /* Watchdog for event loop */
 #endif
 
@@ -94,7 +94,7 @@ static void sim_init_cmdline(void)
  ****************************************************************************/
 
 #if defined(CONFIG_SIM_TOUCHSCREEN) || defined(CONFIG_SIM_AJOYSTICK) || \
-    defined(CONFIG_SIM_BUTTONS)
+    defined(CONFIG_SIM_BUTTONS) || defined(CONFIG_SIM_MOUSE)
 static void sim_x11event_work(void *arg)
 {
   sim_x11events();
@@ -331,7 +331,7 @@ void up_initialize(void)
 #endif
 
 #if defined(CONFIG_SIM_TOUCHSCREEN) || defined(CONFIG_SIM_AJOYSTICK) || \
-    defined(CONFIG_SIM_BUTTONS)
+    defined(CONFIG_SIM_BUTTONS) || defined(CONFIG_SIM_MOUSE)
   work_queue_wq(g_work_queue, &g_x11event_work, sim_x11event_work,
                 NULL, SIM_X11EVENT_PERIOD);
 #endif
diff --git a/arch/sim/src/sim/sim_internal.h b/arch/sim/src/sim/sim_internal.h
index 40aa562c18f..9f256838cd6 100644
--- a/arch/sim/src/sim/sim_internal.h
+++ b/arch/sim/src/sim/sim_internal.h
@@ -329,6 +329,14 @@ int sim_tsc_initialize(int minor);
 int sim_tsc_uninitialize(void);
 #endif
 
+/* sim_mouse.c **************************************************************/
+
+#ifdef CONFIG_SIM_MOUSE
+int sim_mouse_initialize(int minor);
+int sim_mouse_uninitialize(void);
+void sim_mouseevent(int x, int y, int buttons, int wheel);
+#endif
+
 /* sim_keyboard.c ***********************************************************/
 
 #ifdef CONFIG_SIM_KEYBOARD
@@ -339,7 +347,8 @@ void sim_kbdevent(uint32_t key, bool is_press);
 /* sim_eventloop.c **********************************************************/
 
 #if defined(CONFIG_SIM_TOUCHSCREEN) || defined(CONFIG_SIM_AJOYSTICK) || \
-    defined(CONFIG_ARCH_BUTTONS) || defined(CONFING_SIM_KEYBOARD)
+    defined(CONFIG_ARCH_BUTTONS) || defined(CONFIG_SIM_KEYBOARD) || \
+    defined(CONFIG_SIM_MOUSE)
 void sim_x11events(void);
 void sim_buttonevent(int x, int y, int buttons);
 #endif
diff --git a/arch/sim/src/sim/sim_mouse.c b/arch/sim/src/sim/sim_mouse.c
new file mode 100644
index 00000000000..851c59f8fa0
--- /dev/null
+++ b/arch/sim/src/sim/sim_mouse.c
@@ -0,0 +1,206 @@
+/****************************************************************************
+ * arch/sim/src/sim/sim_mouse.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* A full X11 mouse emulation for the sim architecture, registering
+ * /dev/mouseN via the standard mouse_lowerhalf_s interface (see
+ * include/nuttx/input/mouse.h). Modeled closely on sim_touchscreen.c
+ * (same registration shape, same X11-button-event source via
+ * sim_x11eventloop.c), but reports which physical button changed
+ * instead of folding every button into one "pen down" contact --
+ * sim_touchscreen.c exists to emulate real touchscreen hardware, which
+ * has no second or third button to report in the first place. Any
+ * consumer that needs to tell a right-click from a left-click (e.g. a
+ * GUI toolkit's context-menu handling) needs this driver, not the
+ * touchscreen one.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <nuttx/debug.h>
+
+#include <nuttx/input/mouse.h>
+
+#include "sim_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEV_FORMAT   "/dev/mouse%d"
+#define DEV_NAMELEN  16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure describes the state of one simulated mouse driver
+ * instance.
+ */
+
+struct sim_dev_s
+{
+  int eventloop;
+  uint8_t minor;                     /* Minor device number */
+  struct mouse_lowerhalf_s lower;    /* Mouse lowerhalf */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Only one simulated mouse is supported so the driver state structure
+ * may as well be pre-allocated.
+ */
+
+static struct sim_dev_s g_simmouse;
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sim_mouse_initialize
+ *
+ * Description:
+ *   Configure the simulated mouse.  This will register the driver as
+ *   /dev/mouseN where N is the minor device number
+ *
+ * Input Parameters:
+ *   minor   - The input device minor number
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int sim_mouse_initialize(int minor)
+{
+  struct sim_dev_s *priv = (struct sim_dev_s *)&g_simmouse;
+  char devname[DEV_NAMELEN];
+  int ret;
+
+  iinfo("minor: %d\n", minor);
+
+  /* Debug-only sanity checks */
+
+  DEBUGASSERT(minor >= 0 && minor < 100);
+
+  /* Initialize the mouse device driver instance */
+
+  memset(priv, 0, sizeof(struct sim_dev_s));
+
+  priv->minor = minor;
+
+  /* Register the device as an input device */
+
+  snprintf(devname, sizeof(devname), DEV_FORMAT, minor);
+  iinfo("Registering %s\n", devname);
+
+  ret = mouse_register(&priv->lower, devname, 1);
+  if (ret < 0)
+    {
+      ierr("ERROR: mouse_register() failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Enable X11 event processing from the IDLE loop */
+
+  priv->eventloop = 1;
+
+  /* And return success */
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sim_mouse_uninitialize
+ *
+ * Description:
+ *   Uninitialized the simulated mouse
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Return OK if success or negative value of the error.
+ *
+ ****************************************************************************/
+
+int sim_mouse_uninitialize(void)
+{
+  struct sim_dev_s *priv = (struct sim_dev_s *)&g_simmouse;
+  char devname[DEV_NAMELEN];
+
+  /* Stop the event loop (Hmm.. the caller must be sure that there are no
+   * open references to the mouse driver.  This might better be done in
+   * close() using a reference count).
+   */
+
+  priv->eventloop = 0;
+
+  /* Un-register the device */
+
+  snprintf(devname, sizeof(devname), DEV_FORMAT, priv->minor);
+  iinfo("Un-registering %s\n", devname);
+
+  mouse_unregister(&priv->lower, devname);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sim_mouseevent
+ ****************************************************************************/
+
+void sim_mouseevent(int x, int y, int buttons, int wheel)
+{
+  struct sim_dev_s *priv = (struct sim_dev_s *)&g_simmouse;
+  struct mouse_report_s sample;
+
+  if (priv->eventloop == 0)
+    {
+      return;
+    }
+
+  iinfo("x=%d y=%d buttons=%02x wheel=%d\n", x, y, buttons, wheel);
+
+  memset(&sample, 0, sizeof(sample));
+  sample.buttons  = (uint8_t)buttons;
+  sample.x        = (int16_t)x;
+  sample.y        = (int16_t)y;
+  sample.wheel    = (int16_t)wheel;
+
+  /* Report data changes */
+
+  mouse_event(priv->lower.priv, &sample);
+}

Reply via email to