From 0b817211148498e8e067a5bdb5f646c19cba36d8 Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Sun, 19 Jul 2026 22:33:55 +0000
Subject: [PATCH 45/71] =?UTF-8?q?fix(macos):=20tick=20the=20clock=20on=20m?=
 =?UTF-8?q?ouse=20events=20=E2=80=94=20reliable=20click=20selection?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Double-click word selection and triple-click line selection were unreliable
(did nothing, selected the wrong line, or left stale selections). Root cause:
the engine detects single/double/triple clicks from the wall-clock delta
between presses (_currentTime - _lastClick), and _currentTime is advanced
only by Terminal::tick(). This frontend ticked only at render time, so at
mouse-press time the clock was stale, the inter-click delta was meaningless,
and the engine's speed-click counter never reliably reached the word/line
case.

Match what the Qt frontend does: TerminalSession::sendMouse{Press,Move,
Release}Event each call terminal().tick(steady_clock::now()) before
forwarding. Do the same in the bridge's three mouse handlers. That is the
whole fix — the engine's own click-timing then drives linear/word/line
selection as designed; the frontend does not reimplement selection. An
earlier draft that fed AppKit's clickCount and built WordWise/FullLine
selections directly was reverted in favour of this upstream-aligned tick.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeG2jwYMX5gdvvtUnx3PJa
---
 src/contour_macos/SessionBridge.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/contour_macos/SessionBridge.cpp b/src/contour_macos/SessionBridge.cpp
index 6d37c3bb..11f8265f 100644
--- a/src/contour_macos/SessionBridge.cpp
+++ b/src/contour_macos/SessionBridge.cpp
@@ -214,8 +214,18 @@ void bridge_send_char(TerminalSession* session, uint32_t codepoint, uint32_t mod
                                       std::chrono::steady_clock::now());
 }
 
+// Every mouse handler ticks the terminal clock to now() before forwarding, exactly as the Qt
+// frontend does (TerminalSession::sendMouse*Event). This is load-bearing for selection: the engine
+// detects single/double/triple clicks (linear/word/line selection) from the wall-clock delta
+// between presses (_currentTime - _lastClick), and _currentTime is ONLY advanced by tick(). On the
+// Qt path every mouse event ticks first, so the delta is real; without it _currentTime is stale
+// (it only advances at render time here), the delta is meaningless, and double/triple-click never
+// reliably reaches the word/line-selection case. Ticking here is the whole fix — the engine's own
+// _speedClicks logic then does word/line selection as designed; the frontend does not reimplement it.
+
 void bridge_mouse_press(TerminalSession* session, int x, int y, uint32_t modifiers)
 {
+    session->terminal().tick(std::chrono::steady_clock::now());
     session->terminal().sendMousePressEvent(toKeyboardModifiers(modifiers).chord,
                                             vtbackend::MouseButton::Left,
                                             vtbackend::PixelCoordinate { { x }, { y } },
@@ -224,6 +234,7 @@ void bridge_mouse_press(TerminalSession* session, int x, int y, uint32_t modifie
 
 void bridge_mouse_move(TerminalSession* session, int x, int y, uint32_t modifiers)
 {
+    session->terminal().tick(std::chrono::steady_clock::now());
     // The engine derives the cell from the pixel position; pass a best-effort cell too.
     auto const cell = session->pixelToCell(x, y);
     session->terminal().sendMouseMoveEvent(toKeyboardModifiers(modifiers).chord,
@@ -234,6 +245,7 @@ void bridge_mouse_move(TerminalSession* session, int x, int y, uint32_t modifier
 
 void bridge_mouse_release(TerminalSession* session, int x, int y, uint32_t modifiers)
 {
+    session->terminal().tick(std::chrono::steady_clock::now());
     session->terminal().sendMouseReleaseEvent(toKeyboardModifiers(modifiers).chord,
                                               vtbackend::MouseButton::Left,
                                               vtbackend::PixelCoordinate { { x }, { y } },
