Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 108 additions & 24 deletions input/drivers/sdl3_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ typedef struct sdl3_input
float x;
float y;
} touches[SDL3_MAX_TOUCH];

/* Pen/stylus state, handled through SDL_EVENT_PEN_*. */
bool pen_in_proximity;
bool pen_down;
/* Last reported position in window coordinates (points). */
float pen_raw_x;
float pen_raw_y;
/* Position in output pixels, matching mouse_abs_*. */
float pen_abs_x;
float pen_abs_y;

/* The SDL_Window input is read against. */
SDL_Window *window;
} sdl3_input_t;

/* Rebuilt on SDL_EVENT_KEYMAP_CHANGED (e.g. system layout switch). */
Expand Down Expand Up @@ -284,7 +297,13 @@ static int16_t sdl3_input_state(
int16_t pressed = 0;

if (id == RETRO_DEVICE_ID_POINTER_COUNT)
return sdl->num_touches ? sdl->num_touches : (sdl->mouse_l ? 1 : 0);
{
if (sdl->num_touches)
return sdl->num_touches;
if (sdl->pen_in_proximity)
return sdl->pen_down ? 1 : 0;
return sdl->mouse_l ? 1 : 0;
}

if (!video_driver_get_viewport_info(&vp))
break;
Expand All @@ -301,6 +320,18 @@ static int16_t sdl3_input_state(
abs_y = (int)(sdl->touches[idx].y * (float)vp.full_height);
pressed = 1;
}
else if (sdl->pen_in_proximity)
{
/* Reading the pen ahead of the mouse fallback dedups
* the mouse state SDL synthesizes from the pen; a real
* mouse click during pen hover is indistinguishable
* from that and reads as unpressed. */
if (idx != 0)
return 0;
abs_x = (int)sdl->pen_abs_x;
abs_y = (int)sdl->pen_abs_y;
pressed = sdl->pen_down;
}
else
{
if (idx != 0)
Expand Down Expand Up @@ -489,9 +520,24 @@ static SDL_Window *sdl3_input_window(void)
return NULL;
}

/* SDL reports mouse and pen coordinates in window coordinates,
* while the video driver's viewport metrics are in output
* pixels. */
static float sdl3_window_pixel_density(sdl3_input_t *sdl)
{
if (sdl->window)
{
float density = SDL_GetWindowPixelDensity(sdl->window);
if (density > 0.0f)
return density;
}

return 1.0f;
}

static void sdl3_poll_mouse(sdl3_input_t *sdl)
{
SDL_Window *win;
float density;
float dx = 0.0f;
float dy = 0.0f;
SDL_MouseButtonFlags btn = SDL_GetMouseState(&sdl->mouse_abs_x, &sdl->mouse_abs_y);
Expand All @@ -513,20 +559,9 @@ static void sdl3_poll_mouse(sdl3_input_t *sdl)
sdl->mouse_rel_x -= (float)sdl->mouse_x;
sdl->mouse_rel_y -= (float)sdl->mouse_y;

/* SDL reports mouse coordinates in window coordinates (points),
* while the video driver's viewport metrics are in output pixels. */
if (!(win = sdl3_input_window()))
win = SDL_GetMouseFocus();

if (win)
{
float density = SDL_GetWindowPixelDensity(win);
if (density > 0.0f && density != 1.0f)
{
sdl->mouse_abs_x *= density;
sdl->mouse_abs_y *= density;
}
}
density = sdl3_window_pixel_density(sdl);
sdl->mouse_abs_x *= density;
sdl->mouse_abs_y *= density;

sdl->mouse_l = (SDL_BUTTON_MASK(SDL_BUTTON_LEFT) & btn) != 0;
sdl->mouse_r = (SDL_BUTTON_MASK(SDL_BUTTON_RIGHT) & btn) != 0;
Expand Down Expand Up @@ -568,6 +603,10 @@ static void sdl3_poll_touch(sdl3_input_t *sdl)
int j, num_fingers = 0;
SDL_Finger **fingers;

/* Pen events are read elsewhere. */
if (devices[i] == SDL_PEN_TOUCHID)
continue;

/* Only SDL_TOUCH_DEVICE_DIRECT is a touchscreen. The two indirect
* types are trackpads, whose fingers are device or cursor-relative. */
if (SDL_GetTouchDeviceType(devices[i]) != SDL_TOUCH_DEVICE_DIRECT)
Expand Down Expand Up @@ -598,6 +637,48 @@ static void sdl3_poll_touch(sdl3_input_t *sdl)
sdl->num_touch_devices = num_direct;
}

/* Polls the pen events. */
static void sdl3_poll_pen(sdl3_input_t *sdl)
{
SDL_Event event;
float density;

while (SDL_PeepEvents(&event, 1, SDL_GETEVENT,
SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS) > 0)
{
switch (event.type)
{
case SDL_EVENT_PEN_PROXIMITY_IN:
sdl->pen_in_proximity = true;
break;
case SDL_EVENT_PEN_PROXIMITY_OUT:
sdl->pen_in_proximity = false;
sdl->pen_down = false;
break;
case SDL_EVENT_PEN_DOWN:
case SDL_EVENT_PEN_UP:
sdl->pen_in_proximity = true;
sdl->pen_raw_x = event.ptouch.x;
sdl->pen_raw_y = event.ptouch.y;
sdl->pen_down = event.ptouch.down;
break;
case SDL_EVENT_PEN_MOTION:
sdl->pen_in_proximity = true;
sdl->pen_raw_x = event.pmotion.x;
sdl->pen_raw_y = event.pmotion.y;
break;
}
}

/* If the pen isn't in proximity, skip calculating its position. */
if (!sdl->pen_in_proximity)
return;

density = sdl3_window_pixel_density(sdl);
sdl->pen_abs_x = sdl->pen_raw_x * density;
sdl->pen_abs_y = sdl->pen_raw_y * density;
}

/* Translates an SDL_Keymod to a RETROKMOD. */
static uint16_t sdl3_translate_mod(SDL_Keymod smod)
{
Expand Down Expand Up @@ -673,6 +754,11 @@ static void sdl3_input_poll(void *data)
* never updates. */
SDL_PumpEvents();

/* Find the SDL window, so that window coordinates can be calculated
* properly. */
if (!(sdl->window = sdl3_input_window()))
sdl->window = SDL_GetMouseFocus();

sdl3_poll_mouse(sdl);
sdl3_poll_touch(sdl);

Expand Down Expand Up @@ -734,14 +820,12 @@ static void sdl3_input_poll(void *data)
sdl3_build_scancode_lut(sdl);
}

/* Neither range is consumed anywhere: sdl3_poll_touch reads finger
* state by polling instead of by event, and pens aren't wired up at
* all. Both fire at device rate for as long as there's contact, so
* left in the queue they grow until SDL's queue fills and starts
* refusing pushes - at which point the events that do matter (quit,
* keys) get dropped along with them. */
SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED);
SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS);
/* Fingers are reported as pointer input from polled state
* (sdl3_poll_touch / SDL_GetTouchFingers), rather than these
* events, so flush the finger events. */
SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED);

sdl3_poll_pen(sdl);
}

static void sdl3_grab_mouse(void *data, bool state)
Expand Down
Loading