Search guides and the APICtrl K
  1. API reference
  2. Native GUI

gfx/mouse_input.h

MouseInputSource: the mouse, every finger and the pen, as separate pointers -- with a pen's pressure, tilt, rotation and eraser. And MouseInactivityDetector, which watches them stop.

#include <sonore/gfx/mouse_input.h>
On this page

Design notes

Why more than one pointer

A plugin editor on a desktop has one pointer, and every control here was written for one. On a touch screen -- a Surface running a DAW, a tablet mixer -- it has as many as there are fingers, and two faders moved by two fingers at once is the first thing anybody tries. A pen adds what a mouse cannot say at all: how hard, and at what angle, which is what a drawing control (an envelope, a wavetable, a hand-drawn LFO) wants.

How it fits the existing tree without changing it

The PRIMARY pointer -- the mouse, the pen, the first finger down -- goes through the window's MouseRouter exactly as a mouse always has, with the router told what it is (MouseRouter::setPointerInput) so the MouseEvents it makes carry the source, the pressure and the PenDetails. Capture, focus, hover, drag-and-drop: all unchanged, and every control works with a pen or a finger with no code of its own.

SECONDARY fingers are delivered here, each with its own capture: the component under the finger when it went down gets that finger's drags and its release, wherever the finger goes, as a slider expects. They take no focus and start no drag-and-drop -- a second finger is a second gesture, not a second keyboard. A control that wants to tell them apart reads MouseEvent::sourceIndex.

Where the platforms' reports come from

Windows
WM_POINTERDOWN / UPDATE / UP / LEAVE / CAPTURECHANGED, for pen and touch. The mouse keeps arriving as WM_MOUSEMOVE and friends (a plugin must not call EnableMouseInPointer: it is process-wide and the host's); the window keeps the mouse's source from those as it hands them to the router. Handling a pen or touch pointer message here is what stops Windows synthesising the legacy mouse messages for it, so nothing is delivered twice. The pointer functions are looked up at run time from user32, so a plugin still loads where they do not exist, and a test can substitute them: tests drive real WM_POINTER messages through a real window with synthesised POINTER_PEN_INFO, through exactly the code the window runs.
macOS
NSEvent's subtype, pressure, tilt and rotation on the ordinary mouse events a tablet sends, and tabletProximity: for the eraser end. Compiled against the shim, never run.
X11
Not available. The X11 peer reads the core protocol's ButtonPress/MotionNotify, which carry no pressure; XInput2 would be needed and the peer does not open it. Every X11 event is a mouse event with no pressure, which is what it always was.

Types 4

One report from a peer about a non-mouse pointer: what happened, where (in the window's logical coordinates), and what the pointer said about itself.

One pointer: the mouse, a finger, or a pen.

Kept by MouseInputSources and updated by the peers as reports arrive, so a component can ask about the pointer it is being driven by -- how hard the pen is pressing NOW, whether this finger is still down -- between events.

Reports given to MouseInputSources::handle update it, and today that is the Windows pen and fingers; the Windows window also keeps the MOUSE's source up to date (position, press, clicks in a row, time) through noteMouse. On macOS and X11 the mouse, and a pen on macOS, go to the router directly and their source keeps its defaults. getComponentUnderMouse answers for the mouse on every platform; for the pen and the primary finger that answer is null -- ask the router (captured, hovered) instead.

Every pointer this process has seen, and the dispatch of their reports.

Process-wide, like the reference's list of mouse sources: there is one mouse and one pen whatever the number of windows, and a finger belongs to whichever window it touched.

class MouseInactivityDetector : private MouseListener

Tells you when the mouse has been still for a while, and when it moves again.

For hiding an overlay of controls on a full-screen visualiser, dimming a transport bar, starting an attract animation -- never for hiding the pointer itself inside a host's window, which is not a plugin's to hide.

Watches its target AND everything inside it, through a MouseListener, so an editor covered in controls is watched as a whole. The clock is read through a function, steady by default and replaceable by a test; tick() is what notices the stillness, and belongs in the editor's own tick.

Functions 1

↑↓ to move↵ to openEvery public name in the headers, and every guide