feat(input): map Ctrl+, to Cmd+, (Preferences) under --map-ctrl-to-cmd - #184
Conversation
TODO.md - In flight: review state of @antonmos's open PRs. #183's blocker is resolved in c8712e9 with one follow-up requested (the connection reset also fires on every reactivation; the latch survives a reset when no modifier is held; a pre-existing phantom drag after a mid-drag disconnect, latent on main too). #182 asked for a per-step deadline, with a ship-now fallback, and collides with the mic branch on divergence (24). #181 and #184 not yet reviewed. Links issue #186. - Upstreaming watch: a dated update superseding the stale "zero open PRs" note. Divergence (23) is upstream via #1476 + #1913 (ConnectionPolicy) with its traps; IronRDP#1969 blocks de-vendoring (22)/(23) and upstreaming (18) and ships in 0.14.0 unless fixed; #1483 closed; overlapping upstream work to evaluate at the next bump (#1951/#1953/#1954 multitransport stack, ironrdp-rdpeai #1645 + #1946 for the mic divergence). vendor/ironrdp-server/CLAUDE.md - (18): upstream status — #1484 green-lit by a peer, held patch 392 commits stale, and blocked on #1969 because upstream Preempt takes the handler for the race. Notes the --fork-workers scope boundary no longer applies. - (12): the #1951 -> #1953 -> #1954 stack as a possible partial de-vendor path, and how it differs from this divergence. Docs only.
8e2d296 to
2f61a34
Compare
…ering - Records the numbering decision: three branches claim vendored divergence (24), so by expected merge order #183 keeps (24), #182 takes (25) and the mic divergence becomes (26); otherwise take the next free number at merge. - #183: round 3 addressed all three follow-ups (verified against 334cc3c, CI green). Remaining asks: document and live-test the synthetic button-up, which lands at the pre-disconnect cursor position and can complete an interrupted drag as a drop; and tests for the connection-edge path. - #182: no reply yet; must renumber to (25). - #184: stacked on #183, review after it merges. - Upstreaming watch: mic divergence (24->26). Docs only.
2f61a34 to
b11a679
Compare
Same divergence-log conflict as the stacked clintcan#184 merge: main added (25) (MS-RDPEAI microphone redirection) at the append point this branch uses for (24) (the per-served-connection input-reset handle). Kept BOTH, (24) then (25) — main's own (25) entry reserves (24) for this PR, so no renumbering is needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ck remap Synthesized mouse click/drag events never called set_flags, so a click while a modifier was held arrived with empty modifierFlags — a genuine Cmd+click didn't open a link in a new tab, Shift+click didn't range-select, and Ctrl+click wasn't a secondary click. button()/post_move() now stamp the held-modifier state onto every mouse event, mirroring the keyboard path in key(), which has always set_flags for exactly this reason. Additionally, under --map-ctrl-to-cmd a plain Ctrl+click is now delivered as Cmd+click (open link in a new tab) instead of a secondary/context click — the mouse analogue of the existing keyboard Ctrl→Cmd remap, via a shared ctrl_to_cmd_flags() bit-swap. Gated identically (Ctrl held, no Cmd/Alt, non-excluded app); the frontmost-exclusion check sits behind the cheap Ctrl-held guards so an ordinary unmodified drag never pays for it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… remap Addresses review on clintcan#183. Blocking — stale held modifiers now cost the mouse, so resync them. Once clicks carry modifier flags, a modifier whose key-up never arrived (the client lost focus while Ctrl was held) turns every left click into a secondary click, and the blast radius was the whole process: `Inner` owns `mods` and is constructed once, nothing reset it, `synchronize()` reconciles Caps Lock only, and the Synchronize PDU carries lock keys only — so it survived disconnect AND reconnect and cleared only on a server restart. `resync_modifiers_if_stale` (top of keyboard()/mouse()) now clears every held non-lock modifier (Caps Lock preserved — a toggle synchronize() owns), posts one FlagsChanged with the emptied set, and drops any outstanding remapped_keys / click latch, on either trigger: * a new connection — capture.rs's per-connection ScreenCaptureUpdates::start calls input::request_modifier_reset(), the same seam that already resets display_suppressed (RdpServerInputHandler has no per-connection hook), so a reconnect is a deterministic clean slate; * an idle gap >= MACRDP_MODS_RESYNC_IDLE_MS (default 10 s; 0 disables), which covers the common case with no disconnect at all. Generous on purpose: a false clear costs one keystroke, a missed one costs every click. Should-fix 1 — latch the verdict at button-down. `mouse_event_flags()` was re-evaluated for the down and the up, and the down itself refreshes LAST_FOCUS_BUNDLE off-thread via update_focus_from_click, so Ctrl+clicking INTO an excluded app could post a Cmd-flagged down and a Ctrl-flagged up. The verdict is now decided once at left-button-down (`left_click_remapped`, the mouse analogue of remapped_keys) and reused for the drag and the up. Should-fix 2 — primary button only. Right/middle clicks carry the real held modifiers; a Ctrl+right-click no longer becomes Cmd+right-click. Should-fix 3 — scroll() now carries the held modifiers too (Shift+scroll, Cmd+scroll), so the "every synthesized mouse event" claim is true. No Ctrl→Cmd swap on scroll: Ctrl+scroll is macOS's own screen-zoom accessibility gesture. Minor — the per-motion cost is gone: post_move() reuses the latch and never calls frontmost_is_excluded(). The gating predicate is split into the pure `should_remap_click(remap_on, ctrl, cmd, alt, excluded)` and unit-tested as a full decision table (only one row remaps), plus a test pinning that the resync preserves Caps Lock. Also fixes a doc comment that had been attached to the wrong fn by the earlier refactor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follow-up to review on clintcan#183 (three items). 1. The connection-edge reset fired on every reactivation. It was requested from ScreenCaptureUpdates::start, but RdpServerDisplay::updates re-runs after every deactivation-reactivation — so a held modifier was cleared on every live resize and on blank recovery, with no user action. Moved to a once-per-SERVED- connection seam in the vendored server (divergence 24): a new `set_input_reset_handle` flag raised at the top of run_connection AND serve_negotiated, next to divergence 13's identical-lifecycle `auto_reconnect_sent = false`. Each calls accept_finalize exactly once with reactivations looping inside it, so it never fires for a reactivation. Not on_accept — that runs for preemption candidates while the live session is still served, so a mere connection attempt could clear the live session's modifiers. capture.rs no longer touches it; main.rs installs the handle via input::modifier_reset_handle(). 2. The previous connection's latch survived a reconnect: the resync returned early when clear_non_lock() reported nothing held, before clearing remapped_keys / left_click_remapped. Restructured so the connection-edge clears are unconditional. 3. Pre-existing phantom drag: left_down/right_down were only ever written by a real button event, so a drop between a left-down and its up posted every move on the next connection as LeftMouseDragged. The connection-edge reset now RELEASES any button still down — a synthetic Up through button(), so it carries the same latched flags the Down did and the click bookkeeping stays paired — before clearing modifiers, rather than merely forgetting the state (macOS itself still believed the button held). Triggers now clear different amounts, per review: the connection edge clears everything; the idle gap clears modifiers ONLY — a click/drag in progress inside a live connection is legitimate state, and clearing the latch there would undo the down/up consistency fix. FlagsChanged is posted only when a modifier was actually held. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comma wasn't in the curated Ctrl→Cmd shortcut set, so Ctrl+, passed through as a literal Ctrl+comma — which macOS apps don't bind (Preferences is Cmd+,), so nothing happened. Add comma (vk 0x2B) to is_remappable_shortcut so Ctrl+, → Cmd+, opens Preferences, matching the rest of the remap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6637927 to
0b358d1
Compare
|
#183 is merged (58db18c), so this one needs a rebase onto current |
clintcan#183 is merged (58db18c), so main now carries this branch's copies of its three commits. Merging main in collapses the effective diff to just the Ctrl+, change (4 files, 8+/7-) — the outcome the review asked for — without rewriting history. Conflicts were the two long summary lines in docs/features.md and docs/known-quirks.md, where this branch's rebased copies collided with the merged clintcan#183 text plus the interrupted-drag caveat added in b43a047. Resolved by taking main's text (caveat retained) and re-applying only the comma additions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Conflicts resolved in 6f1cdd6 — the PR now shows 4 files, +8/−7: just the Ctrl+, change, which I believe is the end state you were after. One deliberate deviation worth flagging: I did this as a merge of The conflicts were the two long summary lines in
🤖 Addressed by Claude Code |
clintcan
left a comment
There was a problem hiding this comment.
Approving — the net change against main is just the comma key added to the remap set, with its test and doc lines, and CI is green. Squash-merging, since the branch also carries duplicate copies of #183's commits (already on main).
Problem
Ctrl+,did nothing under--map-ctrl-to-cmd. Comma wasn't in the curated Ctrl→Cmd shortcut set (is_remappable_shortcut), soCtrl+,passed through as a literal Ctrl+comma — and macOS binds Preferences to Cmd+,, not Ctrl+comma, so no app reacted.Change
Add comma (vk
0x2B) tois_remappable_shortcut, soCtrl+,→Cmd+,(Preferences), consistent with the rest of the remap. Doc comment + unit test updated; CLI/features/quirks docs list comma in the curated set.Testing
cargo build+cargo test input::(passed). Live-verified:Ctrl+,opens Preferences over RDP.Stacked on #183 — merge that first
This branch is rebased on top of #183's branch (
claude/mouse-modifier-flags), so the two no longer conflict on the shared doc lines. Its own change is the single commit2f61a34(4 files, 8+/7−).Because #183's branch lives on the fork, GitHub won't accept it as this PR's base (it must be a branch in clintcan/macrdp), so the base stays
main. Consequence: until #183 merges, "Files changed" here also shows #183's commits. Once #183 lands, GitHub recomputes and this PR collapses to just the comma commit — no rebase needed. Please merge in order: #183, then #184.🤖 Generated with Claude Code