A press that only brings the window to the front draws nothing

From the development repository at 62e0967.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ackandClaude Opus 4.8 committed 2026-10-11 19:32:13 +02:00
1 parent 2de843143d
commit 51e66db9a1
5 files changed
+78 -10

No files matched your search

+32 -1
View File
@@ -16,6 +16,31 @@ const ERASER_BUTTON = 32;
*/
const NOMINAL_PRESSURE = 0.4;
/**
* A press this soon after the window gained focus is taken to be the
* click that brought the window to the front, and draws nothing (Andy,
* 2026-10-11): clicking the page to return from the terminal, with the
* Pen chosen, left a stray dot. Measured on Hollis (Firefox, Linux):
* the window's focus event fires about 4 ms before such a press, the
* document already reporting focus, so neither document.hasFocus() nor
* the pointer's movement (a pen hovers either way) can tell it from an
* ordinary press; only the time since the focus can. A quarter second
* is far longer than that press and far shorter than any deliberate
* stroke made as a separate act after focusing the window.
*/
export const FOCUS_SETTLE_MS = 250;
/**
* The end-to-end tests turn the focusing-click rule off with
* `?no_focus_settle` in the address: a browser driven by WebDriver gains
* window focus on the first pointer action after every load or tab
* switch, so it cannot tell such a switch from a click that raised the
* window, and the rule would refuse legitimate test strokes. The rule
* itself is tested with a dispatched focus event in one tab. A real
* user never sets this, and it disables nothing but this nicety.
*/
const focusSettleOn = !new URLSearchParams(location.search).has('no_focus_settle');
/**
* @typedef {object} PenTarget
* @property {(clientX: number, clientY: number) => [number, number]} toPage
@@ -100,6 +125,8 @@ export function attachPenInput(surface, target) {
let pressY = 0;
/** @type {PointerEvent | null} The eraser's press, not yet erased with. */
let eraseWaiting = null;
/** When the window last gained focus, for the focusing-click rule. */
let lastFocusAt = -Infinity;
/** The eraser's press erases now, if it was waiting. */
function eraseFromPress() {
@@ -188,6 +215,10 @@ export function attachPenInput(surface, target) {
if (activePointer !== null) return refuse(`pointer ${activePointer} still active`);
if (event.target !== surface) return refuse('not on the page');
if (event.pointerType === 'pen') penSeen = true;
// A press that only brought the window to the front draws nothing.
if (focusSettleOn && performance.now() - lastFocusAt < FOCUS_SETTLE_MS) {
return refuse('it brought the window to the front');
}
// Once a pen has been used, a touch is taken to be the resting hand.
if (event.pointerType === 'touch' && penSeen) return refuse('a touch after a pen: the resting hand');
if (event.pointerType === 'mouse' && event.button !== 0) return refuse('not the main button');
@@ -325,7 +356,7 @@ export function attachPenInput(surface, target) {
// learned on his machine before the rule is written (a window focus
// is faked by the test harness on every tab switch, so it cannot be
// learned here). The press trace already carries "in front"/"behind".
window.addEventListener('focus', () => trace('window: focus'));
window.addEventListener('focus', () => { lastFocusAt = performance.now(); trace('window: focus'); });
window.addEventListener('blur', () => trace('window: blur'));
document.addEventListener('visibilitychange', () => trace(`visibility: ${document.visibilityState}`));