mouse: Provide a way to read clicks

A mouse click is defined as pressing the mouse and then releasing it
over a given spot. Add a function the tracks the mouse state and
returns the most recent mouse click, if any.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2025-09-13 08:13:16 -06:00
parent 9b9fcc8c1e
commit 5d4d719064
3 changed files with 189 additions and 0 deletions

View File

@@ -23,7 +23,50 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *evt)
return 0;
}
int mouse_get_click(struct udevice *dev, int *xp, int *yp)
{
struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev);
struct mouse_event event;
int ret;
/* Get one mouse event */
ret = mouse_get_event(dev, &event);
if (ret)
return -EAGAIN; /* No event available */
/* Only process button events for left button */
if (event.type == MOUSE_EV_BUTTON &&
event.button.button == BUTTON_LEFT) {
enum mouse_press_state_t new_state = event.button.press_state;
bool pending = false;
/* Detect press->release transition (click) */
if (uc_priv->left_button_state == BUTTON_PRESSED &&
new_state == BUTTON_RELEASED) {
pending = true;
uc_priv->click_x = event.button.x;
uc_priv->click_y = event.button.y;
}
/* Update button state */
uc_priv->left_button_state = new_state;
/* If we just detected a click, return it */
if (pending) {
if (xp)
*xp = uc_priv->click_x;
if (yp)
*yp = uc_priv->click_y;
return 0;
}
}
return -EAGAIN;
}
UCLASS_DRIVER(mouse) = {
.id = UCLASS_MOUSE,
.name = "mouse",
.per_device_auto = sizeof(struct mouse_uc_priv),
};

View File

@@ -8,6 +8,8 @@
#ifndef _MOUSE_H
#define _MOUSE_H
#include <stdbool.h>
struct udevice;
enum mouse_ev_t {
@@ -29,6 +31,19 @@ enum mouse_press_state_t {
BUTTON_PRESSED,
};
/**
* struct mouse_uc_priv - private data for mouse uclass
*
* @left_button_state: Current state of left button (BUTTON_PRESSED/BUTTON_RELEASED)
* @click_x: X coordinate where the click occurred
* @click_y: Y coordinate where the click occurred
*/
struct mouse_uc_priv {
enum mouse_press_state_t left_button_state;
int click_x;
int click_y;
};
/**
* struct mouse_event - information about a mouse event
*
@@ -77,4 +92,14 @@ struct mouse_ops {
int mouse_get_event(struct udevice *dev, struct mouse_event *event);
/**
* mouse_get_click() - Check if a left mouse button click has occurred
*
* @dev: Mouse device
* @xp: Returns X coordinate of click (can be NULL)
* @yp: Returns Y coordinate of click (can be NULL)
* Returns: 0 if a click has occurred, -EAGAIN if no click pending
*/
int mouse_get_click(struct udevice *dev, int *xp, int *py);
#endif

View File

@@ -94,3 +94,124 @@ static int dm_test_mouse_button(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_mouse_button, UTF_SCAN_PDATA | UTF_SCAN_FDT);
static int dm_test_mouse_click(struct unit_test_state *uts)
{
struct udevice *dev;
struct mouse_event inject;
int x, y;
ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev));
/* put mouse in test mode */
sandbox_mouse_set_test_mode(dev, true);
/* test that no click is detected initially */
ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y));
/* inject a left button press */
inject.type = MOUSE_EV_BUTTON;
inject.button.button = BUTTON_LEFT;
inject.button.press_state = BUTTON_PRESSED;
inject.button.clicks = 1;
inject.button.x = 300;
inject.button.y = 400;
sandbox_mouse_inject(dev, &inject);
/*
* calling mouse_get_click() should not detect a click yet (press
* only)
*/
ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y));
/* inject a left button release */
inject.type = MOUSE_EV_BUTTON;
inject.button.button = BUTTON_LEFT;
inject.button.press_state = BUTTON_RELEASED;
inject.button.clicks = 1;
inject.button.x = 300;
inject.button.y = 400;
sandbox_mouse_inject(dev, &inject);
/* now mouse_get_click() should detect the click */
ut_assertok(mouse_get_click(dev, &x, &y));
ut_asserteq(300, x);
ut_asserteq(400, y);
/* verify no more clicks are pending */
ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y));
return 0;
}
DM_TEST(dm_test_mouse_click, UTF_SCAN_PDATA | UTF_SCAN_FDT);
static int dm_test_mouse_click_no_coordinates(struct unit_test_state *uts)
{
struct udevice *dev;
struct mouse_event inject;
ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev));
/* put mouse in test mode */
sandbox_mouse_set_test_mode(dev, true);
/* inject press and release to create a click */
inject.type = MOUSE_EV_BUTTON;
inject.button.button = BUTTON_LEFT;
inject.button.press_state = BUTTON_PRESSED;
inject.button.clicks = 1;
inject.button.x = 500;
inject.button.y = 600;
sandbox_mouse_inject(dev, &inject);
/* process the press event */
ut_asserteq(-EAGAIN, mouse_get_click(dev, NULL, NULL));
inject.button.press_state = BUTTON_RELEASED;
sandbox_mouse_inject(dev, &inject);
/*
* now test that click is detected without coordinate return
*/
ut_assertok(mouse_get_click(dev, NULL, NULL));
return 0;
}
DM_TEST(dm_test_mouse_click_no_coordinates, UTF_SCAN_PDATA | UTF_SCAN_FDT);
static int dm_test_mouse_right_button(struct unit_test_state *uts)
{
struct udevice *dev;
struct mouse_event inject;
int x, y;
ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev));
/* put mouse in test mode */
sandbox_mouse_set_test_mode(dev, true);
/*
* right button events should not be detected as clicks by
* mouse_get_click()
*/
inject.type = MOUSE_EV_BUTTON;
inject.button.button = BUTTON_RIGHT;
inject.button.press_state = BUTTON_PRESSED;
inject.button.clicks = 1;
inject.button.x = 100;
inject.button.y = 200;
sandbox_mouse_inject(dev, &inject);
ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y));
inject.button.press_state = BUTTON_RELEASED;
sandbox_mouse_inject(dev, &inject);
/* still no click detected since it was right button */
ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y));
return 0;
}
DM_TEST(dm_test_mouse_right_button, UTF_SCAN_PDATA | UTF_SCAN_FDT);