Skip to content

Auto-unkey momentary keyboard PTT/CW on focus loss + modifier-release (#3888). Principle VI.#3890

Merged
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-3888
Jun 28, 2026
Merged

Auto-unkey momentary keyboard PTT/CW on focus loss + modifier-release (#3888). Principle VI.#3890
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-3888

Conversation

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Summary

Fixes #3888

What was changed

Auto-unkey momentary keyboard PTT/CW on focus loss + modifier-release (#3888). Principle VI.

Files modified

  • src/gui/MainWindow.cpp
  • src/gui/MainWindow.h
  • src/gui/MainWindow_Shortcuts.cpp
 src/gui/MainWindow.cpp           |   9 ++++
 src/gui/MainWindow.h             |  13 +++++
 src/gui/MainWindow_Shortcuts.cpp | 114 +++++++++++++++++++++++++++++++++++----
 3 files changed, 125 insertions(+), 11 deletions(-)

Generated by AetherClaude (automated agent for AetherSDR)


🤖 aethersdr-agent · cost: $9.3030 · model: claude-opus-4-8

…#3888). Principle VI.

Closes two pre-existing stuck-TX windows in the momentary keyboard-keying
family (PTT (Hold), CW straight key / paddles), surfaced by #3884's
rebindable PTT-hold key.

1. Focus loss / deactivation while held — the KeyRelease that would un-key
   goes to whatever app gains focus and never reaches our event filter, so
   the flag stayed set and TX stayed keyed. Add failSafeMomentaryKeyingToRx():
   on QEvent::ActivationChange (window deactivated) and, as a macOS
   belt-and-suspenders, QEvent::ApplicationStateChange (app backgrounded),
   clear every momentary flag and issue the matching un-key. It reuses the
   existing requestPttOff / setCw*State paths so the interlock gating and
   Quindar outro still run, and no-ops when nothing is keyed.

2. Modifier-combo binding released modifier-first — a binding like Ctrl+T
   released Ctrl-first delivers a KeyRelease whose QKeySequence(modifiers|key)
   is now plain T and no longer matches Ctrl+T, so the un-key never fired.
   While a momentary action is active, also accept a release matched by the
   bound base key alone (keyEventMatchesActionBaseKey), so releasing any part
   of the combo fails safe to RX.

Client-side only; the un-key terminates in the existing xmit / sendCwKey
paths with their interlock gating intact. A held transmit key must never
strand the transmitter — fail-safe-to-RX is the Principle VI expectation.

Blast radius: risk_score=0.044 on handlePttHoldShortcut, 3 high-risk
affected (top: MainWindow::eventFilter, RadioModel::transmitModel,
RadioModel::isConnected). All are the existing caller/un-key path this
change reuses; no signatures change and the new changeEvent branch is a
separate ActivationChange early-path that never touches the minimal-mode
AppSettings logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff against #3888 and the surrounding shortcut code. This is a clean, well-scoped implementation of both stuck-TX windows the issue describes, and it consistently fails safe to RX. Nice work, and thank you for the thorough comments tying each branch back to the failure mode it closes.

What I verified

  • All referenced APIs exist and are used correctly: ShortcutManager::action(id) (returns Action*, called from a non-const method ✓), Action::currentKey, and the existing setCw*State / requestPttOff(PttSource::Mox) / cwTraceNowMs / nextCwTraceId paths. The failsafe reuses the same un-key paths as the normal KeyRelease branch, so the interlock gating and Quindar outro still run — good call-out in the comment.
  • keyEventMatchesActionBaseKey compares currentKey[0].key() (key half only) against ev->key(), so a combo released modifier-first still un-keys on the base key's release. The branch only ever clears state on KeyRelease, so it can never spuriously key — strictly the safe direction.
  • failSafeMomentaryKeyingToRx early-outs when nothing is keyed, and the three setCw*State(false…) calls each no-op when already released, so calling all of them (plus the ActivationChange + ApplicationStateChange belt-and-suspenders) is idempotent. The double-trigger just no-ops the second time.
  • CI is green on Linux/macOS/Windows + CodeQL on the head commit.

One non-blocking observation

  • changeEvent(ActivationChange) will also fire when an in-app modal (e.g. a QDialog::exec() or message box) deactivates the main window, which would un-key a held momentary key. In practice that's both unlikely (PTT held = actively transmitting) and the correct fail-safe direction, so I'd leave it as-is — just flagging it so it's a known, intended behavior rather than a surprise.

No changes requested. Looks good to merge.


🤖 aethersdr-agent · cost: $2.1970 · model: claude-opus-4-8

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently traced every new branch against #3888. All three fail-safe paths (window-deactivate, app-deactivate, modifier-first combo release) only ever move toward RX — none can key the radio. PTT un-key reuses requestPttOff(Mox) so the interlock + Quindar outro still run; the CW setCw*State(false) calls are idempotent; the modifier-tolerant release fallback is triple-guarded (KeyRelease only, only while active, base-key match) so it can't spuriously key. CI green across Linux/macOS/Windows + CodeQL. Closes the stuck-TX windows in #3888. Principle VI verified.

@aethersdr-agent aethersdr-agent Bot merged commit 515a40a into main Jun 28, 2026
6 checks passed
@aethersdr-agent aethersdr-agent Bot deleted the aetherclaude/issue-3888 branch June 28, 2026 22:16
ten9876 added a commit that referenced this pull request Jul 2, 2026
#3961)

## Release documentation prep for **v26.7.1** (2026-07-02)

29 commits since v26.6.5. Docs-only + version bump — no code behavior
change.

### Version bump 26.6.5 → 26.7.1
The three canonical locations (per `AGENTS.md`):
- `CMakeLists.txt:2` — `project(AetherSDR VERSION 26.7.1)` →
`AETHERSDR_VERSION` (the app's version string)
- `README.md` — Current version line
- `AGENTS.md` — Current version line

### `CHANGELOG.md`
Promoted `[Unreleased]` → **`## [v26.7.1] — 2026-07-02`** in house style
(v-prefix, em-dash, "N commits since…" opener + headline), authored from
all merged PRs and categorized:
- **Added:** 3D stacked-trace spectrum (#3899), 3D dBm scale (#3937),
in-process NVIDIA BNR + AFX download-on-demand (#3902), TX meter
mouse-over readouts (#3936), SWR manual sweep range (#3885), CW sidetone
capture (#3895), KiwiSDR metadata scaffolding (#3898), bridge verbs
(#3920)
- **Changed:** BNR container/NIM backend removed, 60 fps + per-pixel GPU
FFT trace (#3958), FlexLib-sourced model capabilities (#3954/#2177), RX
speaker-latency cut (#3897), Display pane sections (#3935)
- **Fixed:** #3922, #3926, #3941, #3940, #3942, #3921, #3903, #3892,
#3924, #3891, #3890, #3900, #3947
- Fresh empty `[Unreleased]` restored above the release block.
Internal/CI/docs/self-fix PRs (#3931/#3916/#3934/#3929) omitted per
house style.

### `ROADMAP.md`
- Cycle header → "post-v26.7.1"
- NVIDIA BNR removed from **In flight** (shipped this cycle)
- Five v26.7.1 highlights prepended to **Recently shipped**

### `README.md`
- Highlights: GPU spectrum bullet now notes the **per-pixel FFT trace at
up to 60 fps** and the **3D stacked-trace** spectrum mode

### Reviewer notes
- Touches protected paths (`*.md` + `AGENTS.md` Tier-1 +
`CMakeLists.txt`) — needs `@aethersdr/infrastructure` sign-off.
- Tagging `v26.7.1` and cutting the release build are **not** part of
this PR (docs prep only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fail-safe: auto-unkey momentary keyboard PTT/CW on focus loss + modifier-release (Principle VI)

1 participant