Skip to content

Tags: GlassHaven/Haven

Tags

v5.24.80

Toggle v5.24.80's commit message
Bump to v5.24.80: gate Desktop orientation on settled pager page

User report: sliding the screen pager from Keys to Settings briefly
flipped the device to landscape and back to portrait — even though
the Desktop tab was never selected.

Cause: DesktopScreen's orientation LaunchedEffect (v5.24.74) wrote
requestedOrientation = desktopOrientation (LANDSCAPE default) the
moment DesktopScreen entered composition, and the matching
DisposableEffect.onDispose reset it to UNSPECIFIED when it left.
HorizontalPager composes adjacent pages during a swipe gesture, so
any swipe that grazed the Desktop page triggered the lock-and-unlock
cycle without the user ever landing there.

Fix: add isActive parameter and only apply desktopOrientation when
the pager has settled on Desktop. Wired in HavenNavHost from
pagerState.settledPage == pageOf(Screen.Desktop), same pattern
TerminalScreen already uses. While inactive, the page now keeps the
activity at SCREEN_ORIENTATION_UNSPECIFIED — passing through during
a swipe is a no-op.

The orientation toggle button still works while on the Desktop tab.

v5.24.79

Toggle v5.24.79's commit message
Bump to v5.24.79: opt-in keep-screen-on while in a terminal tab (#122)

agevlakh (#122) asked for the display to stay awake while reading
long-running output from a terminal session. New Settings toggle:
"Keep screen on in terminal" (off by default).

When enabled, TerminalScreen sets the host View's keepScreenOn flag
in a DisposableEffect — so the flag is active only while the terminal
is composed and is automatically cleared when the user navigates to
any other surface (Connections, Files, VNC, RDP, Settings). Only the
terminal screen burns battery, not the whole app.

Default off because keepScreenOn drains battery when forgotten —
opt-in matches the request ("when working in the terminal") and
prevents accidental drain after a one-off use.

The volume-keys-for-text-size half of #122 is intentionally not
implemented: pinch-to-zoom already covers terminal text scaling and
the maintainer rejects hijacking system buttons by default.

v5.24.78

Toggle v5.24.78's commit message
Bump to v5.24.78: tunnelled VNC/RDP/SMB auth + leaked sessions on fai…

…lure (#121)

KoriKraut (#121) reported VNC over SSH tunnel failing with "Auth cancel
for methods public key,password" while plain SSH and plain VNC to the
same host both worked, and each retry leaving another undismissable
active session ("5 active sessions"). Two bugs in connectJumpHost.

1. Empty password on the auto-connect path. VNC/RDP/SMB tunnel call
   sites pass "" because the user clicked the VNC profile, not the SSH
   one — so there's no user-typed password to forward. connectJumpHost
   forwarded the "" through to resolveAuthMethod, which fell through to
   Password(""). For SSH profiles that auth with a saved password, JSch
   then exhausted both key and password attempts and bounced.

   Fix: fall back to jumpProfile.sshPassword ?: "" when the caller
   passes an empty password. User-typed passwords (the #116 jump-chain
   path) still take precedence.

2. Failed jump-host sessions leaked. The session was registered before
   auth was attempted; on any throw inside the connect block (auth
   failure, host-key rejection, network) it stayed in the session
   manager forever. Each retry added another.

   Fix: try/catch around the auth flow; on throw, set status ERROR,
   removeSession, rethrow. Mirrors connectSshSilent's existing pattern.

Untested with a real password-auth jump host in this session — fix is
small, mirrors a working pattern, but warrants reporter retest.

v5.24.77

Toggle v5.24.77's commit message
Bump to v5.24.77: explicit USB permission Intent for Android 14+ FIDO2 (

#15)

olmari (#15) hit an immediate crash on a Pixel 9 Fold (Android 15) when
selecting an SSH connection backed by an ED25519 SK key with their YubiKey
plugged in:

    sh.haven.app: Targeting U+ (version 34 and above) disallows
    creating or retrieving a PendingIntent with FLAG_MUTABLE, an
    implicit Intent ... use FLAG_IMMUTABLE.

Cause: FidoAuthenticator.performUsbAssertion built a PendingIntent
around an implicit Intent (`Intent(ACTION_USB_PERMISSION)` with no
package set) and Haven targets API 35. Android 14+ blocks that
combination — UsbManager.requestPermission writes the
EXTRA_PERMISSION_GRANTED flag back into the intent so we have to keep
FLAG_MUTABLE, which means the Intent itself must be made explicit.

Fix: setPackage(context.packageName) on the permission Intent before
wrapping it in the PendingIntent. The broadcast still only reaches our
own receiver (the IntentFilter on ACTION_USB_PERMISSION already
constrained it); Android 14+'s safety check now sees an explicit
target and allows the MUTABLE flag.

Untested with a physical YubiKey on Android 14+ in this session —
relies on olmari (or any FIDO2-key user) to confirm.

v5.24.76

Toggle v5.24.76's commit message
Bump to v5.24.76: VISIBLE_PASSWORD opts Gboard out of autocap+autospa…

…ce (#115)

agevlakh reported the v5.24.56 fix for #115 didn't help: Huawei Pura 70
video showed typed "cd" landing as "CD" and "ip" as "IP " — Gboard's
autocapitalisation and auto-spacing still firing even with NO_SUGGESTIONS
set. Cause: NO_SUGGESTIONS only hides the suggestion strip; Gboard's
silent smart-typing heuristics fire independently.

Fix in termlib: for non-Samsung IMEs, use TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
on top of TYPE_CLASS_TEXT in Secure mode. Visible-password is the standard
Android opt-out from every IME smart behaviour. Samsung-gated IMEs keep
their NO_SUGGESTIONS | AUTO_CORRECT combo to preserve the #110 fix.

Tradeoff: VISIBLE_PASSWORD disables IME composition, so default Secure
mode no longer supports CJK. CJK users should switch to Compose mode
(Settings → Keyboard); voice and swipe similarly belong in Standard mode.

v5.24.75

Toggle v5.24.75's commit message
Bump to v5.24.75: use LocalActivity instead of casting LocalContext

CI lint on v5.24.73 + v5.24.74 failed with:

    LocalContext should not be cast to Activity, use LocalActivity instead

The orientation code in RdpScreen, VncScreen, and DesktopScreen
fetched the activity via LocalContext.current as? Activity. Compose
lint flags this as fragile because the cast fails on test/preview
hosts where the LocalContext is not an Activity. Replaced with
androidx.activity.compose.LocalActivity.current, which returns the
ComponentActivity directly.

activity-compose 1.10.1 (already in libs.versions.toml) supplies
LocalActivity. No behavioural change.

`./gradlew :app:lintArm64Release` now passes locally.

v5.24.74

Toggle v5.24.74's commit message
Bump to v5.24.74: orientation toggle actually sticks

The button shipped in v5.24.73 cycled the icon but the activity
snapped back to landscape every time. Two underlying bugs:

1. MainActivity had no `configChanges` declared, so each rotation
   destroyed + recreated the activity. The DisposableEffect's
   onDispose ran during destruction, reset requestedOrientation to
   UNSPECIFIED, the new activity rotated, repeat. Added the standard
   Compose-app `configChanges` list to the manifest.

2. The multi-session DesktopScreen has a conditional tab bar
   (`if (tabs.size > 1 || !isConnected) DesktopTabBar(...)`) above
   the session content. When isConnected briefly flipped during a
   rotation recomposition the bar appeared/disappeared, shifting
   the session content's slot position in the parent Column and
   triggering Compose to dispose+recreate RdpViewer/VncViewer. Their
   local `remember { mutableStateOf(Landscape) }` reset, and the
   LaunchedEffect immediately wrote Landscape back to the activity.

Source of truth lifted out of the inner composables:
- DesktopViewModel owns desktopOrientation: StateFlow<Int> and
  exposes cycleDesktopOrientation(). DesktopScreen applies it to
  the activity from a stable parent (above the conditional bar).
- RdpScreen / VncScreen (standalone wrappers) own a local remember
  and apply via LaunchedEffect from their own outer scope.
- RdpViewer / VncViewer now take currentOrientation: Int +
  onCycleOrientation: () -> Unit; the toolbar button just renders
  the icon and forwards taps. Removed the local-state and
  LaunchedEffect/DisposableEffect inside the inner composables.

The OrientationMode enum stays per-feature (private) and gains a
`fromActivityValue(Int)` + a top-level `cycleRdpOrientation` /
`cycleVncOrientation` helper exposed for callers that own the state
elsewhere.

Cycle order unchanged: Landscape -> Portrait -> Auto. Default
unchanged: Landscape on session entry. Activity restored to
UNSPECIFIED when the owning composable disposes.

egfx-pr1238-fixtures-2026-04-30

Toggle egfx-pr1238-fixtures-2026-04-30's commit message
Add replay-egfx-pdu triage tool for EGFX_PDU_DUMP_DIR captures

Companion to the dumper added in bd2e52d. Reads a captured .bin (or
batches a whole directory) and prints a one-line summary per file:
ServerPdu variant, surface ids, codec ids, payload sizes — plus, for
WireToSurface1, both the inclusive and the exclusive interpretation
of width/height from the destination_rectangle. That dual print makes
attached fixtures self-document what Devolutions/IronRDP#1238 is
fixing: spec-correct bytes have right=W (exclusive), but today's
ironrdp parses them as InclusiveRectangle and the trait method's
width() returns W+1.

Auto-detects mode from filename (slow_path_bitmap_update_*.bin →
BitmapUpdateData, else ServerPdu); --egfx and --slow-path force a
mode. Exit codes: 0 = all decoded, 1 = none decoded, 2 = mixed.
Useful as a CI gate before sending captures upstream.

Build with `cargo build --features host-cli --bin replay-egfx-pdu`.
Codec1Type / Codec2Type matches are exhaustive so a future ironrdp
codec addition fails the build here and forces an update.

v5.24.73

Toggle v5.24.73's commit message
Bump to v5.24.73: orientation toggle on RDP + VNC session toolbars

A rotate-icon button on the session toolbar (and fullscreen overlay
menu) cycles Landscape -> Portrait -> Auto. Default stays Landscape,
matching the prior #109/surf5726 forced-landscape behaviour the
DesktopScreen had been applying globally; that DisposableEffect is
removed since the per-session composable now manages it (and
restores UNSPECIFIED on dispose).

Choice persists across config changes via rememberSaveable but isn't
yet persisted per profile — closing and reopening a tab resets to
landscape. Per-profile preference can come later.

Implementation: a tiny private OrientationMode enum lives in each
of feature/rdp/RdpScreen.kt and feature/vnc/VncScreen.kt (mirrors,
not shared) with three entries mapping to ActivityInfo's
SCREEN_ORIENTATION_USER_LANDSCAPE / USER_PORTRAIT / UNSPECIFIED.
Feature modules don't depend on each other, so duplicating ~25 LOC
beats inventing a shared module for it.

Rust unchanged, no .so rebuild.

v5.24.72

Toggle v5.24.72's commit message
Bump to v5.24.72: typing works in Windows RDP across both viewer paths

ASCII chars typed into a Windows RDP session were being sent as
TS_FP_UNICODE_KEYBOARD_EVENT, which the Windows console host (cmd,
PowerShell) silently drops — only Enter and other scancode-mapped
keys made it through. Two viewer paths needed the fix because the
Desktop multi-session view (DesktopScreen) bypassed the standalone
RdpScreen wrapper.

New shared helper `typeRdpChar(ch, sendKey, sendUnicode)` in
feature/rdp/RdpKeyMapping.kt maps US-layout ASCII to AT Set 1
scancodes (with shift handling for capitals + shifted symbols), and
falls back to the Unicode path for non-ASCII. Both
RdpScreen.kt:259 and DesktopScreen.kt:170 onTypeChar handlers now
call it.

Rust unchanged, so no librdp_transport.so rebuild needed for this
release.