Skip to content

Mouse coordinates are in window points while Pencuil hit tests in render pixels #462

Description

@botoddly

Problem

Mouse input coordinates and the Pencuil viewport are expressed in two different coordinate spaces, which agree today only because high pixel density is never requested.

SDL3 reports mouse coordinates in window coordinates (logical points): SDL_MouseMotionEvent.x/y, SDL_MouseButtonEvent.x/y, and SDL_MouseWheelEvent.mouse_x/mouse_y. MouseService passes these through unconverted, and PencilSystem feeds them straight into Pencil.UpdateCursor((Vector2Int)args.Position).

Pencil, meanwhile, works in render pixels. PencilSystem.Update sizes the viewport from Window.RenderSizeInPixels (SDL_GetWindowSizeInPixels, src/Pixely/Window.cs:59), and PencuilRenderer builds its projection from the same value. Every hit test in Pencil — hover areas, click tests, hover patches, scroll areas — compares point-space cursor positions against pixel-space rectangles.

The two spaces are separate SDL concepts: Window.Size uses SDL_GetWindowSize (points) while Window.RenderSizeInPixels uses SDL_GetWindowSizeInPixels (pixels).

Why it does not reproduce today

PixelyFactory never sets SDL_WINDOW_HIGH_PIXEL_DENSITY (src/Pixely/PixelyFactory.cs:128-162) and WindowConfig exposes no option for it. Without that flag SDL keeps the window in low-DPI mode, so SDL_GetWindowSizeInPixels matches SDL_GetWindowSize and the mismatch cancels out.

This is therefore a latent defect, not a user-visible one. It becomes real as soon as SDL_WINDOW_HIGH_PIXEL_DENSITY is supported, on a 2x display every cursor position would be reported at half the coordinates the GUI is laid out in, so hit testing would be offset and wrong across the whole window.

Proposed direction

Convert mouse coordinates into render pixels at the boundary rather than at each consumer, so a single conversion covers hit testing, wheel routing, and any future pointer consumer. The scale factor is the ratio between SDL_GetWindowSizeInPixels and SDL_GetWindowSize (or SDL_GetWindowPixelDensity).

Doing this before high pixel density support lands keeps the change independent of the DPI work itself.

Acceptance criteria

  • Mouse event positions delivered to consumers are in the same coordinate space as Window.RenderSizeInPixels.
  • Pencuil hit testing stays correct when the window pixel size differs from its point size.
  • The conversion happens once at the input boundary rather than per consumer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions