Skip to content

feat: disable IME extract UI and centralize singleLine keyboard behavior - #1014

Merged
utkarshdalal merged 2 commits into
utkarshdalal:masterfrom
jeremybernstein:jb/fix-textfield-ime
Apr 13, 2026
Merged

feat: disable IME extract UI and centralize singleLine keyboard behavior#1014
utkarshdalal merged 2 commits into
utkarshdalal:masterfrom
jeremybernstein:jb/fix-textfield-ime

Conversation

@jeremybernstein

@jeremybernstein jeremybernstein commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Description

adds NoExtractOutlinedTextField wrapper that sets IME_FLAG_NO_EXTRACT_UI, opting out of the IME's fullscreen extract editing mode. centralizes ImeAction.Done + clearFocus() for singleLine fields, removing per-callsite boilerplate. migrates all OutlinedTextField usages across the app (11 files).

closes #1013.

Recording

before:
https://github.com/user-attachments/assets/c195692d-3120-44bb-8221-a6fc94844d3e

after:
https://github.com/user-attachments/assets/b96dcd62-6adc-4b44-a26c-66b3969b2695

Test plan

  • Open any container config dialog, type in a text field, delete mid-word — no extract UI overlay
  • Single-line fields show "Done" IME action and dismiss keyboard on tap
  • Multi-line field (game feedback) still works normally without Done action

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by CodeRabbit

  • New Features

    • Text inputs now prevent full-screen IME/keyboard extraction on devices, keeping surrounding UI visible during typing.
  • Refactor

    • Replaced standard outlined text fields across dialogs, forms, and settings (presets, env vars, login, feedback, managers) with the improved input component while preserving existing behaviors and layouts.

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ea4a248-1e40-4139-b20e-96d81a91e847

📥 Commits

Reviewing files that changed from the base of the PR and between adef332 and 7d1b73b.

📒 Files selected for processing (8)
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/DrivesTab.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/WorkshopManagerDialog.kt
  • app/src/main/java/app/gamenative/ui/component/settings/SettingsTextFieldWithSuggestions.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/settings/ContentsManagerDialog.kt
  • app/src/main/java/app/gamenative/ui/screen/settings/DriverManagerDialog.kt
  • app/src/main/java/app/gamenative/ui/screen/settings/WineProtonManagerDialog.kt
✅ Files skipped from review due to trivial changes (4)
  • app/src/main/java/app/gamenative/ui/component/dialog/WorkshopManagerDialog.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/DrivesTab.kt
  • app/src/main/java/app/gamenative/ui/screen/settings/ContentsManagerDialog.kt

📝 Walkthrough

Walkthrough

Adds NoExtractOutlinedTextField, a Compose wrapper that opts out of the IME extract UI and centralizes single-line IME behavior; replaces many OutlinedTextField usages across dialogs, settings, and login screens with this wrapper.

Changes

Cohort / File(s) Summary
New Component
app/src/main/java/app/gamenative/ui/component/NoExtractOutlinedTextField.kt
Added NoExtractOutlinedTextField which intercepts platform text input to OR EditorInfo.IME_FLAG_NO_EXTRACT_UI, maps ImeAction.DefaultImeAction.Done for single-line fields, and defaults Done to focusManager.clearFocus() when keyboardActions not supplied.
Dialog & Form Inputs
app/src/main/java/app/gamenative/ui/component/dialog/Box64PresetsDialog.kt, .../FEXCorePresetsDialog.kt, .../ContainerConfigDialog.kt, .../WorkshopManagerDialog.kt, .../DrivesTab.kt, .../EnvironmentTab.kt, .../GameFeedbackDialog.kt, .../GeneralTab.kt, .../TouchGestureSettingsDialog.kt, .../ControllerBindingDialog.kt
Replaced OutlinedTextField usages with NoExtractOutlinedTextField; set singleLine = true where appropriate; preserved existing bindings, labels, icons, suggestion/dropdown logic, and persistence calls.
Settings UI
app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt, .../SettingsTextFieldWithSuggestions.kt
Switched text inputs to NoExtractOutlinedTextField, added singleLine = true at call sites, retained modifiers, focus/requester, value, and onChange behavior.
Login & Auth Screens
app/src/main/java/app/gamenative/ui/screen/login/TwoFactorAuthScreen.kt, app/src/main/java/app/gamenative/ui/screen/login/UserLoginScreen.kt
Migrated username/password/2FA inputs to NoExtractOutlinedTextField; preserved filtering, visualTransformation, focus and keyboard wiring (2FA IME action adjusted).
Other UI Screens
app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt, .../settings/ContentsManagerDialog.kt, .../settings/DriverManagerDialog.kt, .../settings/WineProtonManagerDialog.kt
Replaced Material3 OutlinedTextField imports/usages with NoExtractOutlinedTextField for read-only selectors and input fields while keeping call-site parameters identical.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant IME
    participant Interceptor as InterceptPlatformTextInput
    participant TextField as NoExtractOutlinedTextField
    participant Focus as FocusManager

    User->>IME: enter text / trigger IME action
    IME->>Interceptor: PlatformTextInputMethodRequest (EditorInfo)
    Interceptor-->>IME: forward modified EditorInfo (imeOptions OR-ed with IME_FLAG_NO_EXTRACT_UI)
    Interceptor->>TextField: deliver input events / composition
    TextField->>User: render updated text
    alt singleLine && IME action == Done
        TextField->>Focus: clearFocus()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through code with whiskers bright and spry,
Turned off fullscreen IME so text won't lie.
Single-line Done now clears focus with a wink,
No extract overlay, no stale-text blink.
Hooray — the fields behave; I twitched my nose, goodbye!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: disable IME extract UI and centralize singleLine keyboard behavior' clearly and specifically summarizes the main change in the changeset.
Description check ✅ Passed The description includes all key sections: summary of changes, recording links (before/after), test plan with specific test cases, and a completed checklist with relevant items checked.
Linked Issues check ✅ Passed The PR fully addresses the objectives from issue #1013: disables IME extract UI via IME_FLAG_NO_EXTRACT_UI flag and centralizes singleLine keyboard behavior with ImeAction.Done and clearFocus().
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the NoExtractOutlinedTextField wrapper and migrating OutlinedTextField usages across 11 files as specified in the linked issue objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 11 files

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt (1)

42-49: Consider parameterizing singleLine for flexibility.

The hardcoded singleLine = true works for most use cases (button labels, typical settings). However, this component is also used for environment variable values (per SettingsEnvVars.kt), which could theoretically contain longer or multi-part values.

Given the 76.dp width constraint already implies compact single-line usage, this is acceptable. If multi-line env var support is needed later, consider exposing singleLine as a parameter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt`
around lines 42 - 49, The NoExtractOutlinedTextField usage hardcodes singleLine
= true; make this configurable by adding a singleLine: Boolean parameter to the
SettingsTextField composable (default true) and forward it into
NoExtractOutlinedTextField, then update call sites (e.g., SettingsEnvVars usage)
to pass singleLine = false where multi-line values may be needed; ensure default
preserves current behavior.
app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt (1)

69-91: Consider adding ImeAction.Next to navigate between width and height fields.

Both fields use singleLine = true without explicit imeAction, so pressing "Done" on the width field will clear focus instead of moving to the height field. Users can still tap the height field manually, but adding ImeAction.Next with focus navigation would improve the input flow.

♻️ Optional: Add focus navigation between width and height
+val heightFocusRequester = remember { FocusRequester() }
+
 NoExtractOutlinedTextField(
-    modifier = Modifier.width(128.dp),
+    modifier = Modifier.width(128.dp).focusRequester(widthFocusRequester),
     value = state.customScreenWidth.value,
     onValueChange = { state.customScreenWidth.value = it },
-    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+    keyboardOptions = KeyboardOptions(
+        keyboardType = KeyboardType.Number,
+        imeAction = ImeAction.Next,
+    ),
+    keyboardActions = KeyboardActions(
+        onNext = { heightFocusRequester.requestFocus() },
+    ),
     label = { Text(text = stringResource(R.string.width)) },
     singleLine = true,
 )
 // ... separator ...
 NoExtractOutlinedTextField(
-    modifier = Modifier.width(128.dp),
+    modifier = Modifier.width(128.dp).focusRequester(heightFocusRequester),
     value = state.customScreenHeight.value,
     // ... rest remains the same with singleLine = true for Done behavior
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt` around
lines 69 - 91, The width and height NoExtractOutlinedTextField inputs
(NoExtractOutlinedTextField using state.customScreenWidth.value and
state.customScreenHeight.value) lack an explicit IME action, so pressing "Done"
on the width field clears focus instead of moving to the height field; update
the first field's KeyboardOptions to include imeAction = ImeAction.Next and add
KeyboardActions (or a FocusRequester/LocalFocusManager) to move focus to the
height field on Next, and set the height field's imeAction appropriately (e.g.,
ImeAction.Done) so keyboard navigation flows from the width field to the height
field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt`:
- Around line 69-91: The width and height NoExtractOutlinedTextField inputs
(NoExtractOutlinedTextField using state.customScreenWidth.value and
state.customScreenHeight.value) lack an explicit IME action, so pressing "Done"
on the width field clears focus instead of moving to the height field; update
the first field's KeyboardOptions to include imeAction = ImeAction.Next and add
KeyboardActions (or a FocusRequester/LocalFocusManager) to move focus to the
height field on Next, and set the height field's imeAction appropriately (e.g.,
ImeAction.Done) so keyboard navigation flows from the width field to the height
field.

In `@app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt`:
- Around line 42-49: The NoExtractOutlinedTextField usage hardcodes singleLine =
true; make this configurable by adding a singleLine: Boolean parameter to the
SettingsTextField composable (default true) and forward it into
NoExtractOutlinedTextField, then update call sites (e.g., SettingsEnvVars usage)
to pass singleLine = false where multi-line values may be needed; ensure default
preserves current behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9daec99-4575-41e4-ab4f-3fa8b6c81e0a

📥 Commits

Reviewing files that changed from the base of the PR and between c7da303 and a9471d6.

📒 Files selected for processing (11)
  • app/src/main/java/app/gamenative/ui/component/NoExtractOutlinedTextField.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/Box64PresetsDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/ControllerBindingDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/EnvironmentTab.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/FEXCorePresetsDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GameFeedbackDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/TouchGestureSettingsDialog.kt
  • app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt
  • app/src/main/java/app/gamenative/ui/screen/login/TwoFactorAuthScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/login/UserLoginScreen.kt

@jeremybernstein

jeremybernstein commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

🧹 Nitpick comments (2)

@coderabbitai

#1: ok
#2: YAGNI, not changing

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@jeremybernstein: Understood on both!

  1. GeneralTab.kt — glad the ImeAction.Next suggestion is useful; sounds good.
  2. SettingsTextField.kt — noted, YAGNI makes sense here. I'll remember not to suggest making singleLine configurable in SettingsTextField in future reviews.

✏️ Learnings added
Learnt from: jeremybernstein
URL: https://github.com/utkarshdalal/GameNative/pull/1014

Timestamp: 2026-03-25T10:11:17.504Z
Learning: In `app/src/main/java/app/gamenative/ui/component/settings/SettingsTextField.kt`, the `singleLine = true` hardcoded in the `NoExtractOutlinedTextField` usage is intentional (YAGNI). Do not suggest adding a `singleLine` parameter to `SettingsTextField` or making it configurable in future reviews.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: unbelievableflavour
Repo: utkarshdalal/GameNative PR: 811
File: app/src/main/java/app/gamenative/ui/util/WindowSize.kt:38-44
Timestamp: 2026-03-11T11:17:48.473Z
Learning: In `app/src/main/java/app/gamenative/ui/util/WindowSize.kt`, `shouldShowGamepadUI()` reads `PrefManager.showGamepadHints` as a plain (non-reactive) property. This is intentional: when the user toggles the setting in the Settings screen and navigates back, the composable recomposes and picks up the updated value. Real-time reactivity (e.g., via StateFlow/collectAsState) is not required here. Do not flag this as an issue in future reviews.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 1005
File: app/src/main/java/app/gamenative/ui/component/dialog/PhysicalControllerConfigSection.kt:706-738
Timestamp: 2026-03-24T12:55:58.820Z
Learning: In `app/src/main/java/app/gamenative/ui/component/dialog/PhysicalControllerConfigSection.kt`, the `needsCopy` heuristic in `copyElementsIfNeeded` (around lines 706-738) treats any destination layout lacking `GAMEPAD_*` bindings as safe to overwrite with source elements, even if it contains non-GAMEPAD custom bindings. This imperfect heuristic is a pre-existing limitation acknowledged by the maintainer and intentionally out of scope. Do not flag this overwrite-without-merge behavior as an issue in future reviews of this file.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 1005
File: app/src/main/java/app/gamenative/ui/component/dialog/ControllerPresetManager.kt:54-166
Timestamp: 2026-03-24T12:55:21.360Z
Learning: In `app/src/main/java/app/gamenative/ui/component/dialog/ControllerPresetManager.kt`, the three hardcoded factory presets (`PRESET_DEFAULT`, `PRESET_FPS`, `PRESET_PLATFORMER`) with embedded English names are a known, pre-existing limitation. The lack of centralization with profile assets and the absence of localized display names are acknowledged but intentionally out of scope. Do not flag the hardcoded English preset names or the duplication between factory presets and seeded profile assets as issues in future reviews of this file.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 801
File: app/src/main/java/app/gamenative/utils/BestConfigService.kt:45-51
Timestamp: 2026-03-10T21:46:14.981Z
Learning: In `app/src/main/java/app/gamenative/utils/BestConfigService.kt`, the singleton mutable state pattern (`lastMissingComponents` / `consumeLastMissingComponents()`) — where `parseConfigToContainerData()` writes a global and callers read it via `consume*()` — is a pre-existing design. Refactoring it to return missing components as part of the parse result has a large blast radius and is intentionally deferred. Do not flag this pattern as an issue in future reviews of this file.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 801
File: app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt:174-188
Timestamp: 2026-03-10T22:12:03.164Z
Learning: In `app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt`, the `applyConfigForContainer` function calls `installMissingComponentsForConfig` before `BestConfigService.parseConfigToContainerData`. This install-before-parse ordering is pre-existing behavior, not introduced by any recent PR. Do not flag this ordering as an issue in future reviews of this file.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 0
File: :0-0
Timestamp: 2026-03-10T22:13:54.453Z
Learning: In `app/src/main/java/app/gamenative/utils/BestConfigService.kt`, around lines 403-418, missing Box64/FEXCore preset failures (via `Box86_64PresetManager.getPreset("box64", ...)` and `FEXCorePresetManager.getPreset(...)`) are intentionally appended to the same `missingComponents` list as installable components, even though `resolveMissingManifestInstallRequests()` cannot install presets. This is a known limitation deferred for future work — the long-term direction is to serialize non-default presets into config files and auto-install them. Do not flag this mixing of installable vs. non-installable blockers in `missingComponents` as an issue in future reviews of this file.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 0
File: :0-0
Timestamp: 2026-03-18T16:30:26.610Z
Learning: In `app/src/main/java/app/gamenative/ui/PluviaMain.kt`, the three sites that call `MainActivity.setPendingLaunchRequest`, toggle `shownPendingLaunchSnackbar`, and conditionally call `SnackbarManager.show` (cold-start pending branch, ExternalGameLaunch pending branch, and non-terminal SteamDisconnected branch) are intentionally kept separate — the surrounding logic differs enough that extracting a shared helper would add more complexity than it removes. Do not suggest refactoring these into a helper in future reviews.

Learnt from: unbelievableflavour
Repo: utkarshdalal/GameNative PR: 810
File: app/src/main/java/app/gamenative/ui/screen/library/appscreen/CustomGameAppScreen.kt:391-417
Timestamp: 2026-03-18T08:52:17.299Z
Learning: In the `app/src/main/java/app/gamenative/ui/screen/library/appscreen/` AppScreen files (e.g., `BaseAppScreen.kt`, `CustomGameAppScreen.kt`), using `CoroutineScope(Dispatchers.IO).launch` inside `AppMenuOption` click handlers (such as for `FetchSteamGridDBImages` and `CreateShortcut`) is an established, intentional pattern. Do not flag this as a lifecycle-safety issue in future reviews of these files.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 0
File: :0-0
Timestamp: 2026-03-18T16:30:26.610Z
Learning: In `app/src/main/java/app/gamenative/MainActivity.kt`, `peekPendingLaunchRequest()` is an intentional non-destructive, synchronized read of `pendingLaunchRequest`. The peek + separate consume pattern (also present at line 436) is pre-existing and accepted; the worst-case outcome of a race is an unnecessary snackbar, which is benign. Do not flag `peekPendingLaunchRequest()` as a TOCTOU race issue in future reviews.

Learnt from: jeremybernstein
Repo: utkarshdalal/GameNative PR: 801
File: app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt:105-143
Timestamp: 2026-03-11T07:31:50.139Z
Learning: In `app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt`, the force-apply path inside `BaseAppScreen.showMissingComponentsDialog`'s `onApplyAnyway` callback (re-parsing with `forceApply = true`, resolving manifest installs, and writing the container) is intentional and user-initiated. It only runs when the user explicitly taps "Apply anyway" in the missing-components dialog. Do not flag this as contradicting the reject-on-missing-components goal; the default is rejection and this is an explicit user override.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Is this the only way we can do this? Is there no way to use OutlinedTextField with a param or something?

@utkarshdalal
utkarshdalal merged commit b0989a2 into utkarshdalal:master Apr 13, 2026
3 checks passed
@jeremybernstein
jeremybernstein deleted the jb/fix-textfield-ime branch April 13, 2026 13:41
xXJSONDeruloXx pushed a commit to xXJSONDeruloXx/GameNative that referenced this pull request Apr 28, 2026
…ior (utkarshdalal#1014)

* feat: disable IME extract UI and centralize singleLine keyboard behavior

* fix: convert remaining OutlinedTextField to NoExtractOutlinedTextField
utkarshdalal added a commit that referenced this pull request Jun 15, 2026
* feat: use game hero image as booting splash background

* fix: add drop shadow to booting splash status and tip text

* feat: resolve hero image for all game sources on booting splash

* Update app/src/main/java/app/gamenative/ui/model/MainViewModel.kt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Added verify files to GOG, clear prerequisite markers on verify, allow submitting feedback for non-steam, non-custom games

* fix: do not crash on game start on Meta Quest (#1105)

* Don't show logged out steam splash when offline and steam games are installed (#1138)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Hide local saves only setting (#1139)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Added back best config for non steam games, except remove executable path if it's a different store (#1141)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* bumped version code for 0.9 full release

* fix: prevent carousel touch loss during pagination (#1127)

* fix: prevent carousel touch loss during pagination by removing touch-conflicting draggable modifier

* fix: address bot review feedback on carousel mouse input handler

* fix: add drag slop to carousel mouse drag to prevent accidental scrolls on click

* fix: Download support files for gen 2 (#1130)

* Download support files for gen 2

* Added tests to verify downloading support and game files for gen 1 and 2

* fix: include shared redistributables when resolving steam depots (#1166)

* fix: include shared redistributables when resolving steam depots

Merges depot IDs from the shared license (ID 0) with app-specific depots to ensure common redistributables and dependencies are correctly identified and downloaded along with game-specific files.

* Update app/src/main/java/app/gamenative/service/SteamService.kt

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* fix: use horizontal tab row for downloads screen in portrait mode (#1153)

* Added wine env var fix type + fix for stardew (#1156)

* fix: improve download memory handling on CaseInsensitiveFileSystem (#1142)

* do less caching for god of war download

* bump js version 1.8.0.1-18-SNAPSHOT, update CaseInsensitiveFileSystem to implement BaseCaseInsensitiveFileSystem

* only remove the file from cache inside removeFileCache

* fix: nested segment cache + pre-populated listings in CaseInsensitiveFileSystem

* update resolveAndCache only cache directory and skip file

* add log option to CaseInsensitiveFileSystem, handle deleteRecursively properly

* coderabbitai comments

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>

* chore: re-order downloads & storage tabs (#1079)

* Reorder sidebar: Storage Manager first, Downloads second; fix header title on Downloads tab

* Default selected tab to Storage Manager

* Add downloads_section_title translations for all 13 locales

* fix: swap Settings before Downloads in System sidebar

* chore: add fexcore 2604 (#1170)

Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>

* feat: empty Wine/XDG trash on container shutdown (#1151)

* Feat: Update wine-mono version to 11.0.0 in installer scripts (#1133)

* feat: update wine-mono version to 11.0.0 in installer scripts

* fix: bump_version in ImageFsInstaller.java,
will trigger extraction for all existing containers that are on Mono 9.0.0 and install 11.0.0.

* Added additional directory for xna msi (#1125)

* feat: warn-before-exit preference with double-back confirmation (#993)

* Update README.md

* fix: match UFS save file globs case-insensitively (#1183)

* feat: ludashi style vivid screen effect (#1106)

* feat: add fake HDR screen effect

* fix: remove quick menu darkening scrim

* chore: shorten HDR effect description

* refactor: rename HDR effect to vivid mode

* i18n: add vivid mode translations

* Add GOG force cloud save and related string updates. (#812)

* fix: library view not updating after game uninstall (#956)

DownloadService caches directory listings for 5s. After deleteApp,
the cache still holds the deleted directory, so the subsequent
LibraryInstallStatusChanged refresh sees stale data. Invalidate
the cache after deletion so the next scan picks up the change.

* fix: correct steam game dlc licensing logic and enhance dlc display (#1191)

* fix: correct steam game dlc licensing logic and enhance dlc display in content

Cross-references resolved depots with owned DLC package information to ensure depots are attributed to the correct DLC app ID. This ensures accurate DLC identification for titles like Don't Starve, Halo MCC, and Cyberpunk 2077.

* refactor getMainAppDepots to calculate the logic to be used in getDownloadableDepots

* fix: suppress connection banner during initial Steam connect (#918)

Also use state.isSteamConnected (Compose-observable StateFlow) instead
of SteamService.isConnected (static boolean invisible to recomposition)
for banner visibility.

* Devil blade reboot utkarsh (#1198)

* fix: case-insensitive .exe filter in getWindowsLaunchInfos

* removed bug around appLaunchInfo null opening wfm.exe

* fixed build

* addressed coderabbit

* more coderabbit

---------

Co-authored-by: Dan Brooke <mail@danbrooke.net>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: Migrate GSE Saves to steam userdata, always upload userdata files to steamcloud (#1100)

* migrate GSE Saves to steam userdata, always upload userdata files to steam cloud

fix tests

* move migrateGSESavesToSteamUserdata just before beginLaunchApp

* also migrateGSESavesToSteamUserdata just before forceSyncUserFiles

* also migrateGSESavesToSteamUserdata in SteamUtils ensureSteamSettings

* use Files.move for migrating files

* check dir empty to exit earlier, update logging

* preserve file attributes like timestamp and permission during migration

* Add reusable INI game fix for Imperivm (#1009)

* Add reusable ini game fix for Imperivm

* Avoid rereading ini fixes after migration

* Remove ini migration marker tracking

* feat: disable IME extract UI and centralize singleLine keyboard behavior (#1014)

* feat: disable IME extract UI and centralize singleLine keyboard behavior

* fix: convert remaining OutlinedTextField to NoExtractOutlinedTextField

* fix: preserve file timestamps during Steam Cloud download (#1199)

Maintains the original modification time from Steam Cloud for downloaded files. This ensures that games which rely on file timestamps for save loading and ordering, such as Skyrim, function correctly.

* feat: added multi-controller support (#1047)

* feat: added multi-controller support

- physical controller handler services now acommodate multi controller
state management via deviceId
- new autoassign func
- windhandler accomodated MAX_PLAYERS = 4 suppot, multi-controller state
management
- new MultiControllerTest.kt test suite

fix: address multi-controller rumble and device identifier bugs

- prevent missed rumble on secondary slots when controller is adopted
    after a rumble command arrives by deferring the "delivered" marker
  - remove unreachable vendor/product fallback in device identifier
    lookup since getDescriptor() is available on all supported API
levels

revert to original controller manager handling legacy cases and added test case

* handled respect disabled slots when reusing an existing assignment

* reset rumble state when a slot adopts a new controller

* magic number removed using dedicated WinHandler.MAX_PLAYERS const

* removed more magic numbers - using dedicated MAX_PLAYERS const and remove null controller phone vibrate case with some docs on why

---------

Co-authored-by: = <=>

* fix: use container language for install size estimate (#1054)

size estimate used PrefManager.containerLanguage (global default) but
download uses container.language (per-container). mismatch causes wrong
depot count and size when container language differs from default.

* feat: FSR 1.0 & Scaling Modes (#1112)

* feat: add quick menu fsr and sharpness controls

* refactor: port fsr passes from official gpuopen source

* feat: add true fsr render-scale upscaling

* chore: remove fsr toggle description

* refactor: derive fsr input from container resolution

* feat: add live scaling modes

* chore: move scaling controls to top of screen effects

* fix: match default fsr rcas denoise behavior

* fix: save quick menu scrim removal before master sync

* fix(renderer): only override render target size for scaled scenes

* fix: clear stale container state on task swipe and app restart (#1136)

* fix: clear stale container state on task swipe and app restart

when the user swipes the app from the task switcher while a container
is running, keepAlive stays true but xEnvironment is gone. on next
launch the app is stuck thinking a container is running.

extract shutdownEnvironment() from XServerScreen.exit() so the same
full teardown runs in both the normal exit path and the crash recovery
path. each step is wrapped in runCatching so one failure doesn't
prevent the rest.

- onCreate: if keepAlive is set but xEnvironment is null, run
  shutdownEnvironment() to clear stale state
- onDestroy: emit ActivityDestroyed before super (so exit() listeners
  still fire), then force shutdownEnvironment() if keepAlive persists
- exit(): delegates teardown to shutdownEnvironment(), keeps only
  winHandler.stop() and trash cleanup (container-specific)

* fix: stop all foreground services on task swipe when idle

Steam/GOG/Epic services had no onTaskRemoved — foreground notification
persisted after swipe because nothing told the service to stop itself.

* fix: recognize unhandled UFS path types and fix save pattern parsing edge cases (#1157)

* fix: recognize WindowsHome UFS root as PathType.Root for cloud save sync

Steam PICS can specify `root: WindowsHome` in save file patterns (e.g.
Stellar Blade, app 3489700). PathType.from() did not handle this token,
causing it to fall through to PathType.None. None.isWindows is false, so
the pattern was silently dropped in getLocalUserFilesAsPrefixMap and the
saves were never scanned or synced.

WindowsHome is the Windows user home directory (C:\users\xuser\ in Wine),
which is exactly what PathType.Root maps to. Fix by recognising
"windowshome", "%windowshome%", and "root" in PathType.from() as Root,
and adding Root to the isWindows set so it passes the save pattern filter.

* fix: recognize SteamCloudDocuments UFS root as WinMyDocuments for cloud save sync

Steam PICS can specify `root: SteamCloudDocuments` in save file patterns
(e.g. Sonic Mania, app 584400). PathType.from() did not handle this token,
causing it to fall through to PathType.None and be silently dropped during
save pattern filtering.

SteamCloudDocuments is Steam's name for the user's Documents folder, which
maps to WinMyDocuments (C:\users\xuser\Documents\) in Wine. Fix by
recognising "steamclouddocuments" and "%steamclouddocuments%" in
PathType.from() as WinMyDocuments.

* fix: normalize '.' save path to empty string to prevent broken cloud keys

Steam PICS manifests sometimes use `path: .` to mean "root of this path
type, no subdirectory" (common in Unity games). When a Windows rootoverride
also has a non-empty addpath, the dot was appended literally — producing
paths like "Thunder Lotus Games/Spiritfarer/." and uploadPath = "." —
which caused cloudPrefixToLocalPath to build a key like "%GameInstall%."
that never matches the bare "%GameInstall%" prefix the cloud API returns,
so downloaded files landed in the wrong directory.

Fix by normalising "." to "" at parse time in KeyValueUtils, consistent
with how UserFileInfo.prefix already treats cloudPath == ".". Affected
games: Spiritfarer and CrossCode.

* fix: recognize WinProgramData and SteamUserBaseStorage UFS path types, handle oslist in rootoverrides

- Add WinProgramData PathType mapping to drive_c/ProgramData/
- Alias SteamUserBaseStorage to SteamUserData in PathType.from()
- Check oslist field alongside os when filtering Windows rootoverrides

* fix: bump CURRENT_UFS_PARSE_VERSION to 2 to force re-parse of cached UFS data

Ensures existing cached SteamApp rows are re-parsed to pick up the path
normalization and oslist rootoverride fixes from this branch.

* fix: lowercase Root/ROOT_MOD aliases and add wrapped %root% form in PathType.from()

* fix: add %steamuserbasestorage% as suggested by coderabbit

* fix: perf hud fps fix for other wrappers (#1164)

* fix(hud): measure fps from render frames

* fix(hud): track fps against topmost app window

* Fix silent cloud save overwrite when sync cache is missing (#1169)

* fix: show conflict dialog when cloud sync cache is missing, regardless of change number

the old gate (localAppChangeNumber >= 0) skipped the conflict dialog
for first-time offline players whose change number is -1. this meant
local saves were silently overwritten by cloud on reconnect. remove
the gate: if cache is absent and local files exist, always treat as
conflict.

* test: cloud sync decision matrix covering all cache/CN/cloud states

12 scenarios covering: cache present/absent, local changes/none,
cloud ahead/same, preferred save location, first-time offline play.
also fixes existing download test (cloud filenames, mock params,
deprecated API).

* fix: split cache-absent conflict into upgrade vs first-offline cases

* test: revert modifications to existing tests, keep only new additions

* fixed steam intent launches, no need to check for offline mode

* Added option to toggle button hints bar (#811)

* fix crashes on migrateGSESavesToSteamUserdata (#1207)

* GOG chunk URL broken when CDN token is in query string (#1215)

* Update CONTRIBUTING.md to reduce ambiguity

* Update pull_request_template.md to reduce ambiguity

* Jb/streaming assembly utkarsh (#1219)

* feat: streaming download+assembly for Epic/GOG to reduce disk usage

replace two-phase (download all chunks → assemble all files) with a
unified loop that assembles files front-to-back as their chunks land
and deletes consumed chunks immediately. peak disk usage drops from
~2x install size to ~1x.

- StreamingAssembly: shared pure logic for chunk ordering, last-file
  tracking, readiness checks, and safe deletion decisions
- EpicDownloadManager: downloadAndAssembleEpicChunks replaces separate
  download+assembly phases for both base game and DLC
- GOGDownloadManager: downloadAndAssembleChunks with secure link
  refresh, used by both main game and dependency downloads. removed
  dead downloadChunksSimple and assembleFiles.
- 14 unit tests covering ordering, deduplication, shared chunks,
  cleanup safety, and full batch-loop simulations

* fix: run final assembly pass for zero-chunk Epic files

mirrors the GOG fix — when all files have zero chunks, the chunk loop
never executes and assembly never runs without a trailing pass.

* fix: allow all-zero-chunk Epic manifests to reach assembly

* Resolved conflicts, sped up GOG, made resuming downloads for GOG work better, made downloading UI progress smoother

* handle retries correctly for assembly for GOG

* addressed coderabbit comments

* More AI fixes

---------

Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Aligned epic downloads to GOG to make it faster from eg India (#1220)

* Aligned epic downloads to GOG to make it faster from eg India

* coderabbit comments, removed xserverscreen mistake changes

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Revert "feat: added multi-controller support (#1047)" (#1224)

This reverts commit 2334981.

* Create ROADMAP.md

* Update ROADMAP.md

* tests: Added unit tests for new key parts of Gamenative (#1143)

* Added launch dependency tests

* Added game fix registry tests

* Added preinstall step tests

* Some AI improvements + moved gamefixes tests to new types folder

* Added test to keep canonical root at the correct location (#1144)

* fix: preserve aspect-correct viewport for screen effects (#1213)

* Create pr-label-command.yml

* Recommendation page (#1235)

* Added recommendations to game page + library, added toggle to hide recommendations,

* Added review scores to recommended games

* Added date to recommended app screen like the others

* coderabit comments

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: skip spurious conflict when cache lost but local==remote (#1228)

destructive db migration wipes file_change_lists cache, making every
steam game trigger conflict on first launch post-update. check if
local state is byte-identical to remote manifest (by filename + SHA)
before declaring conflict; if so, rehydrate cache and report UpToDate
silently. also populates real timestamps on the genuine-divergence
path (was showing epoch).

test: dbCleared_localMatchesRemote_rehydratesSilently_noConflict

* chore(): openApi specs for Gog, Epic & Amazon (#1234)

* chore(): openApi specs for Gog, Epic & Amazon

* chore(): Update the epic token to look more fake.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* added toggle to disable posthog tracking (#1236)

* added toggle to disable posthog tracking

* Updated readme to include analytics info

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* refactor: Moved Proton downloading to launch deps (again, but mirroring original behaviour + tests) (#1052)

* Reapply "Refactored default Proton downloads to launch deps" (#1050)

This reverts commit 1cd84b4.

* Moved proton download to original location by moving launch deps call

* Removed deletion since previously we didnt do that either.

* Added test for new launch dep

* Added codeonwers file

* fix(): toggle showing achievements (#1251)

* fix(): toggle showing achievements

* fix(): fixed imports and state on settings. Testing now.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: mouse movement in games that use raw mouse input and clipcursor (#1084)

* refactor: register X extensions with event and error id assignment

* add writeFP3232 to XOutputStream

* feat: add XInput2 Extension and associated events

* Implement ClipCursor behavior and fix XWarpPointer to include soft margin in the window calculations

* add logging to help extension debugging

* fix: correct confinement off by one

* fix: throw BadValue if no mask as per XI2 spec

* fix: change Bitmask to hold 64 bits to be able to read XI2 masks

* More fallout game fixes (#1134)

* fix: reject config with missing components, show dialog with apply-anyway option (#801)

* fix: reject config with missing components, offer apply-anyway with defaults

* fix: pass storeMatch to force-apply path for cross-store configs

* Added kingdom hearts 3 (EPIC) gamefix (#1161)

* feat: Add Steam save import and export actions (#966)

* Add Steam save import and export actions

* fix: harden Steam save import and export

* fix: correct manifest driver id

* fix: stabilize Steam save archive roots

* fix: make Steam save root ids collision resistant

* fix: simplify Steam save transfer to userdata

* feat: add multi-root save discovery and translations

* refactor: use string resources for app option menu labels

* Workshop Update: Manual Mod Folder Dialog (#1072)

* Workshop Update: Manual Mod Folder Dialog

- Full-screen workshop manager dialog with hero image, mod list,
  select-all/deselect-all, and per-mod toggle switches
- FolderPickerDialog for manually choosing mod installation paths
  when automatic detection fails
- Improved mod path detection: binary scanning, config file parsing,
  AppData walking with fuzzy matching
- CKM (Creation Kit Module) extraction for Skyrim mods
- LZMA decompression with concurrent processing
- ZIP extraction for single-archive workshop uploads
- Magic-byte file type detection and extension fixing
- Disk space checking before downloads
- allSelected derivedStateOf optimization (removed wasteful toMap key)
- Added KNOWN_EXTENSIONS to WorkshopItem for file type validation

* Fix folder picker breadcrumb for multi-level paths

* Guard folder picker directory listing with try/catch/finally

* Rethrow CancellationException in folder picker LaunchedEffect

* feat: task manager from qam (#1121)

* feat(quick-menu): replace tools tab with task manager launcher

* feat(quick-menu): show running exe memory usage

* feat(ui): streamline quick menu task manager

* refactor: inline process user lookup in ProcessHelper

* fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)

* feat: extract XAudio DLLs from DirectX redistributables

Adds support for decompressing and installing DirectX audio components (XAudio, XACT, X3DAudio) from game cabinet files into the Wine prefix. This uses 7-Zip bindings to extract the necessary DLLs to system folders, helping resolve audio compatibility issues in games that rely on specific redistributable versions.

* refactor replaceXAudioDllsFromRedistributable to XServerScreenUtils

add other gamesource detection

* fix gameId detection

* surround gameId detection with try catch

* update use FileOutputStream for dll extraction

* add log when appDirPath detection failed

* move SevenZip init outside per file function

* ai comments

* only apply for proton 10, fix proton 9.0 compatibility

* revert SteamUtils changes

* fix directXDir detection logic

* fix directXDir detection logic

* update proton 10 logic

* fix: kill stale wine processes before launch (#1195)

* fix: kill stale wine processes before launch

* fix: wait for stale wine processes to exit

* fix: block back during prelaunch loading

* refactor: move stale wine process kill logic to ProcessHelper

* fix: GOG cloud save fetch failure handling (#1201)

* Fix GOG cloud save fetch handling

* Add GOG cloud save regression tests

* Clarify GOG cloud save fallback comment

* feat: Parallelize Steam cloud save downloads (#1226)

* Refactor Steam cloud download flow

No logic changes. Extract the per-file download body from the forEach loop in
downloadFiles into a new private downloadSingleFile function to make the
upcoming parallelisation diff easier to read.

The httpClient is moved from steamInstance.steamClient.configuration.httpClient
(pulled inline) to a parameter so the caller controls which client to use.

* Parallelize Steam cloud save downloads

Replace the sequential forEach loop in downloadFiles with a
coroutineScope { map { async { semaphore.withPermit { ... } } }.awaitAll() }
pattern, capping concurrency at PrefManager.downloadSpeed via a Semaphore.

A dedicated OkHttpClient is created per sync via Net.httpForParallelDownloads
so each parallel call gets its own Dispatcher thread pool.

filesDownloaded and bytesDownloaded are promoted from plain vars to AtomicInteger
and AtomicLong to handle concurrent updates safely.

Per-file streaming progress is added: downloadedRawBytes (incremented per
chunk via a CAS loop) drives onProgress during the download, while a separate
lastReportedPercent AtomicInteger deduplicates callbacks so only strictly
increasing percentages are emitted. The upfront indeterminate "Downloading
filename" callback is removed in favour of this.

downloadSingleFile gains httpClient, totalRawBytes, downloadedRawBytes,
lastReportedPercent, completedFiles, totalFiles, and progressMessage
parameters to support the above. The unused steamInstance parameter is
removed. copyTo now returns total bytes read and passes chunkBytes to the
progress callback.

Adds steam_cloud_sync_downloading_save_files string to all 14 locale files
for the X/Y files in-progress message.

* Fix cloud save download edge cases

Wrap the connection phase (httpClient.newCall.execute) in a try/catch for
SocketTimeoutException and IOException, returning null on failure instead of
propagating an uncaught exception.

Capture the withTimeout(responseTimeout) block as a Boolean result and return
null if the response body stream was absent. Previously a null body silently
completed without writing any file; now both the compressed and uncompressed
paths guard with ?: return@withTimeout false.

Move response.close() to a finally block so it fires on every exit path.
Remove the redundant explicit close that was on the !isSuccessful path.

Fix UserFilesDownloadResult to report rawFileSize (uncompressed bytes) instead
of fileSize (compressed bytes). The mismatch caused bytesDownloaded to
undercount for compressed files and allowed the download progress to
transiently exceed 100%.

Emit "Download complete" unconditionally after awaitAll() rather than only
when filesDownloaded == totalFiles, so a partial failure no longer stalls the
UI at the last reported percentage.

Add IOException and SocketTimeoutException catches inside the streaming try
block (distinct from the outer connection-phase catches) with a shared finally
for response.close().

* Fix cloud save download robustness issues

Close response on unsuccessful HTTP to prevent connection leaks, catch
TimeoutCancellationException so one download timeout doesn't cancel all
parallel downloads, treat short reads as failures and clean up partial
files, and only report "Download complete" when all files succeeded.

* fix: address Steam cloud review feedback

Handle Steam cloud metadata fetch failures per file without swallowing coroutine cancellation, so the reviewed exception path no longer cancels sibling downloads while structured cancellation still works correctly.

Also tear down the per-sync Steam cloud download client after the batch completes to release dispatcher threads and pooled connections, while keeping failed-download file handling aligned with current master behavior.

* fix: use stored installPath in GOGManager.deleteGame to prevent uninstall failures (#1255)

* added new turnip drivers to manifest (#1263)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* chore(): add new box64 to GN. (#1262)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Revert "fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)" (#1266)

This reverts commit 3a4cb3d.

* Added some changes for rockstar launcher (#1274)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Updated manifest to include new protons (#1275)

* Updated manifest to include new protons

* Fixed manifest values

* fixed proton 10 x86-64 id

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Added THIRD_PARTY_NOTICES

* feat/refactor: Improve GOGDownloadManager download efficiency (#1277)

* refactor: Improve GOGDownloadManager download efficiency

1. Adopt Flow queuing concept from JavaSteam
2. add DownloadSpeedConfig to be used later for other store enhancement
3. update kotlinx-coroutines-core version to match JavaSteam version using
4. Update NetworkUtils httpForParallelDownloads for timeout and http protocol config

* ai comments

* add retry logic when same pendingChunks appear 10 times in a row

* fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding (#1269)

* Reapply "fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)" (#1266)

This reverts commit 93793fa.

* fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding

Replaces the 7-Zip JBinding library with a Wine-based batch script that uses the native cabarc utility to extract DirectX DLLs. This removes the dependency on JitPack and the external 7-Zip library while maintaining the fix for Proton 10.

* ai comments

* ai comments

* ai comments, refactor to XAudioUtils

* guard batchCommand with BATCH_SUCCESS_CHECK

* move guard code location after all extraction

* remove useless comment

* add splash text when extracting dlls

* fix(workshop): force standard Steam UGC path for Tale of Immortal (#1260)

- Add AppID 1468810 (Tale of Immortal) to `forceStandardAppIds` to ensure workshop items are handled via the standard ISteamUGC path.

* Move Touchscreen Mode toggle to in-game sidebar (#1249)

Moved Touchscreen Mode toggle from Edit Container > Controller section to the in-game sidebar, right below the existing Edit On-screen Controller line. Added gear icon for gesture settings that appears when touchscreen mode is active.

* fix: late release single tap in touchscreen mode to fix clicking in some games (#1212)

* fix: late release single tap in touchscreen mode to fix clicking in some games

* fix: immediately release click before a new single tap

* clear delayedPress state after execution

* flush pending single tap release on handleTsDown

* Update README.md

* feat(): Silly draft for ideation of EOS.

* Added sidecar for EOS, made downloading EOS launch dependency, Deliver at all Costs working

* addressed AI comments

* fix: gog download pause / resume handling logic

* fix unit test

* revert downloadChunk logic on md5 checking before downloading file

* Made layouts for appscreen and libraryscreen cutout/notch aware

* Merge pull request #1300 from utkarshdalal/unpack-more

made unpack files more aggressive

* fix: resolve cloud save path for Danganronpa 2 (app 413420) (#1297)

Danganronpa 2 stores saves in WinMyDocuments/My Games/Danganronpa2/ via
a Windows rootoverride, but GameNative was placing downloaded cloud saves
in the game install directory instead, so the game never found them.

Two bugs fixed:

1. KeyValueUtils: treat PICS path '/' as empty (same as '.')
   A lone forward-slash means 'root of this path type' with no subdir.
   Keeping the literal '/' caused uploadPath='/' which put a trailing
   slash on the cloudPrefixToLocalPath map key ('%GameInstall%/') while
   the lookup trimmed it ('%GameInstall%'), causing a miss. With the fix,
   uploadPath='' and path='My Games/Danganronpa2' (no trailing slash).
   Also bumps CURRENT_UFS_PARSE_VERSION 2->3 to force cache refresh.

2. SteamAutoCloud getFullFilePath: consult cloudPrefixToLocalPath when
   Steam embeds the placeholder in the filename (prefix=[],
   filename='%GameInstall%savedata.vfs'). The previous early-return
   hardcoded the destination as the game install dir, bypassing all
   rootoverride remapping. Now checks cloudPrefixToLocalPath['%GameInstall%']
   first so the file lands in WinMyDocuments/My Games/Danganronpa2/.

Also adds .trimEnd('/') to cloudKey construction in cloudPrefixToLocalPath
so map keys are always slash-free (matching the existing lookup behaviour).

Tests added:
- KeyValueUtilsTest: danganronpa2SlashPathWithWindowsRootOverrideIsNormalizedToEmpty
- SteamAutoCloudTest: downloadWithEmbeddedGameInstallPrefixUsesRootoverrideLocalPath
- keyvalues/Danganronpa 2 Goodbye Despair.txt: PICS reference documentation

* fix/tabbar-scrolling-clip (#1293)

* fix: copy a new dll generated with proton 11 (#1287)

* fix: open download details fullscreen (#1270)

* fix: repair steam save export (#1265)

* feat: move Disable Mouse Input to in-game Quick Menu overlay (#1267)

* feat: move Disable Mouse Input to in-game Quick Menu overlay

Removes the Disable Mouse Input toggle from the container settings
Controller tab and adds it to the in-game Quick Menu, where it can
be toggled during gameplay without leaving the game. State is
persisted to the container config on toggle.

* fix: address code review feedback

- Remove unconditional setCursorVisible call; cursor state is managed
  by existing pointer-availability logic and should not be overridden
  when a physical mouse may be present
- Key isDisableMouseInput state to container.id to prevent stale state
  if container changes

* fix: restore cursor visibility on mouse input toggle

Show cursor when re-enabling mouse input (unless touchscreen mode is
also active), hide it when disabling. Required for touch-as-mouse
users who have no physical pointer device.

* fix: use mouse icon and pink accent for Disable Mouse quick menu item

* fix: join prealloc job before polling so zero-chunk files are created before return (#1308)

* fix: use consistent purple accent color for all quick menu items (#1311)

* Stop overwriting wine prefix when switching between containers of dif… (#1310)

* Stop overwriting wine prefix when switching between containers of different variants/archs, only extract xaudio dlls once

* do reinstall of mono and preinstallsteps on wine version change (not arch), fixed bugs with wincomponents, audio driver, and startup selection being overwritten on wine version change

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: soften boot splash hero backdrop

* fix: resolve boot splash hero image off main thread

* made booting splash image greyscale background instead of having inconsistent color that doesn't gel with GN

---------

Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Luboš V. <tridosm@gmail.com>
Co-authored-by: UnbelievableFlavour <bart.zaalberg@shift2.nl>
Co-authored-by: Joshua Tam <297250+joshuatam@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>
Co-authored-by: CatPotatos <catarinaleal123@gmail.com>
Co-authored-by: AnikethanVA <82267223+AnikethanVA@users.noreply.github.com>
Co-authored-by: Dan Brooke <mail@danbrooke.net>
Co-authored-by: Misazam <60115666+Misazam@users.noreply.github.com>
Co-authored-by: bllendev <113651082+bllendev@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: André Vitor <90573731+AndreVto@users.noreply.github.com>
Co-authored-by: Nightwalker743 <Hillardjoseph43@gmail.com>
Co-authored-by: linkq <mrlinkq@hotmail.com>
Co-authored-by: Daniel Joyce <danielalexanderjoyce@gmail.com>
Co-authored-by: tlt21 <travistryba@gmail.com>
Co-authored-by: Almond <88301593+sdkahal@users.noreply.github.com>
Co-authored-by: Ben Pearson <ben@buriza.co.uk>
utkarshdalal added a commit that referenced this pull request Jun 20, 2026
* Implementing SettingsListDropdownSearchable.
A Searchable ListDropDown for Dropdowns with a lot of items.

Exchanged SettingsListDropdown with SettingsListDropdownSearchable in following dialogs
GraphicsTab->Graphic-Driver
Winetab->Renderer
WineTab->GPU-Name

Added Translations for Label.

* Resolving issues from AI-Codereview

* Resolving Translation Issuis from AI-Codereview

* Update app/src/main/res/values-ko/strings.xml

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Feat/hero bg booting splash utkarsh (#1587)

* feat: use game hero image as booting splash background

* fix: add drop shadow to booting splash status and tip text

* feat: resolve hero image for all game sources on booting splash

* Update app/src/main/java/app/gamenative/ui/model/MainViewModel.kt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Added verify files to GOG, clear prerequisite markers on verify, allow submitting feedback for non-steam, non-custom games

* fix: do not crash on game start on Meta Quest (#1105)

* Don't show logged out steam splash when offline and steam games are installed (#1138)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Hide local saves only setting (#1139)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Added back best config for non steam games, except remove executable path if it's a different store (#1141)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* bumped version code for 0.9 full release

* fix: prevent carousel touch loss during pagination (#1127)

* fix: prevent carousel touch loss during pagination by removing touch-conflicting draggable modifier

* fix: address bot review feedback on carousel mouse input handler

* fix: add drag slop to carousel mouse drag to prevent accidental scrolls on click

* fix: Download support files for gen 2 (#1130)

* Download support files for gen 2

* Added tests to verify downloading support and game files for gen 1 and 2

* fix: include shared redistributables when resolving steam depots (#1166)

* fix: include shared redistributables when resolving steam depots

Merges depot IDs from the shared license (ID 0) with app-specific depots to ensure common redistributables and dependencies are correctly identified and downloaded along with game-specific files.

* Update app/src/main/java/app/gamenative/service/SteamService.kt

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* fix: use horizontal tab row for downloads screen in portrait mode (#1153)

* Added wine env var fix type + fix for stardew (#1156)

* fix: improve download memory handling on CaseInsensitiveFileSystem (#1142)

* do less caching for god of war download

* bump js version 1.8.0.1-18-SNAPSHOT, update CaseInsensitiveFileSystem to implement BaseCaseInsensitiveFileSystem

* only remove the file from cache inside removeFileCache

* fix: nested segment cache + pre-populated listings in CaseInsensitiveFileSystem

* update resolveAndCache only cache directory and skip file

* add log option to CaseInsensitiveFileSystem, handle deleteRecursively properly

* coderabbitai comments

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>

* chore: re-order downloads & storage tabs (#1079)

* Reorder sidebar: Storage Manager first, Downloads second; fix header title on Downloads tab

* Default selected tab to Storage Manager

* Add downloads_section_title translations for all 13 locales

* fix: swap Settings before Downloads in System sidebar

* chore: add fexcore 2604 (#1170)

Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>

* feat: empty Wine/XDG trash on container shutdown (#1151)

* Feat: Update wine-mono version to 11.0.0 in installer scripts (#1133)

* feat: update wine-mono version to 11.0.0 in installer scripts

* fix: bump_version in ImageFsInstaller.java,
will trigger extraction for all existing containers that are on Mono 9.0.0 and install 11.0.0.

* Added additional directory for xna msi (#1125)

* feat: warn-before-exit preference with double-back confirmation (#993)

* Update README.md

* fix: match UFS save file globs case-insensitively (#1183)

* feat: ludashi style vivid screen effect (#1106)

* feat: add fake HDR screen effect

* fix: remove quick menu darkening scrim

* chore: shorten HDR effect description

* refactor: rename HDR effect to vivid mode

* i18n: add vivid mode translations

* Add GOG force cloud save and related string updates. (#812)

* fix: library view not updating after game uninstall (#956)

DownloadService caches directory listings for 5s. After deleteApp,
the cache still holds the deleted directory, so the subsequent
LibraryInstallStatusChanged refresh sees stale data. Invalidate
the cache after deletion so the next scan picks up the change.

* fix: correct steam game dlc licensing logic and enhance dlc display (#1191)

* fix: correct steam game dlc licensing logic and enhance dlc display in content

Cross-references resolved depots with owned DLC package information to ensure depots are attributed to the correct DLC app ID. This ensures accurate DLC identification for titles like Don't Starve, Halo MCC, and Cyberpunk 2077.

* refactor getMainAppDepots to calculate the logic to be used in getDownloadableDepots

* fix: suppress connection banner during initial Steam connect (#918)

Also use state.isSteamConnected (Compose-observable StateFlow) instead
of SteamService.isConnected (static boolean invisible to recomposition)
for banner visibility.

* Devil blade reboot utkarsh (#1198)

* fix: case-insensitive .exe filter in getWindowsLaunchInfos

* removed bug around appLaunchInfo null opening wfm.exe

* fixed build

* addressed coderabbit

* more coderabbit

---------

Co-authored-by: Dan Brooke <mail@danbrooke.net>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: Migrate GSE Saves to steam userdata, always upload userdata files to steamcloud (#1100)

* migrate GSE Saves to steam userdata, always upload userdata files to steam cloud

fix tests

* move migrateGSESavesToSteamUserdata just before beginLaunchApp

* also migrateGSESavesToSteamUserdata just before forceSyncUserFiles

* also migrateGSESavesToSteamUserdata in SteamUtils ensureSteamSettings

* use Files.move for migrating files

* check dir empty to exit earlier, update logging

* preserve file attributes like timestamp and permission during migration

* Add reusable INI game fix for Imperivm (#1009)

* Add reusable ini game fix for Imperivm

* Avoid rereading ini fixes after migration

* Remove ini migration marker tracking

* feat: disable IME extract UI and centralize singleLine keyboard behavior (#1014)

* feat: disable IME extract UI and centralize singleLine keyboard behavior

* fix: convert remaining OutlinedTextField to NoExtractOutlinedTextField

* fix: preserve file timestamps during Steam Cloud download (#1199)

Maintains the original modification time from Steam Cloud for downloaded files. This ensures that games which rely on file timestamps for save loading and ordering, such as Skyrim, function correctly.

* feat: added multi-controller support (#1047)

* feat: added multi-controller support

- physical controller handler services now acommodate multi controller
state management via deviceId
- new autoassign func
- windhandler accomodated MAX_PLAYERS = 4 suppot, multi-controller state
management
- new MultiControllerTest.kt test suite

fix: address multi-controller rumble and device identifier bugs

- prevent missed rumble on secondary slots when controller is adopted
    after a rumble command arrives by deferring the "delivered" marker
  - remove unreachable vendor/product fallback in device identifier
    lookup since getDescriptor() is available on all supported API
levels

revert to original controller manager handling legacy cases and added test case

* handled respect disabled slots when reusing an existing assignment

* reset rumble state when a slot adopts a new controller

* magic number removed using dedicated WinHandler.MAX_PLAYERS const

* removed more magic numbers - using dedicated MAX_PLAYERS const and remove null controller phone vibrate case with some docs on why

---------

Co-authored-by: = <=>

* fix: use container language for install size estimate (#1054)

size estimate used PrefManager.containerLanguage (global default) but
download uses container.language (per-container). mismatch causes wrong
depot count and size when container language differs from default.

* feat: FSR 1.0 & Scaling Modes (#1112)

* feat: add quick menu fsr and sharpness controls

* refactor: port fsr passes from official gpuopen source

* feat: add true fsr render-scale upscaling

* chore: remove fsr toggle description

* refactor: derive fsr input from container resolution

* feat: add live scaling modes

* chore: move scaling controls to top of screen effects

* fix: match default fsr rcas denoise behavior

* fix: save quick menu scrim removal before master sync

* fix(renderer): only override render target size for scaled scenes

* fix: clear stale container state on task swipe and app restart (#1136)

* fix: clear stale container state on task swipe and app restart

when the user swipes the app from the task switcher while a container
is running, keepAlive stays true but xEnvironment is gone. on next
launch the app is stuck thinking a container is running.

extract shutdownEnvironment() from XServerScreen.exit() so the same
full teardown runs in both the normal exit path and the crash recovery
path. each step is wrapped in runCatching so one failure doesn't
prevent the rest.

- onCreate: if keepAlive is set but xEnvironment is null, run
  shutdownEnvironment() to clear stale state
- onDestroy: emit ActivityDestroyed before super (so exit() listeners
  still fire), then force shutdownEnvironment() if keepAlive persists
- exit(): delegates teardown to shutdownEnvironment(), keeps only
  winHandler.stop() and trash cleanup (container-specific)

* fix: stop all foreground services on task swipe when idle

Steam/GOG/Epic services had no onTaskRemoved — foreground notification
persisted after swipe because nothing told the service to stop itself.

* fix: recognize unhandled UFS path types and fix save pattern parsing edge cases (#1157)

* fix: recognize WindowsHome UFS root as PathType.Root for cloud save sync

Steam PICS can specify `root: WindowsHome` in save file patterns (e.g.
Stellar Blade, app 3489700). PathType.from() did not handle this token,
causing it to fall through to PathType.None. None.isWindows is false, so
the pattern was silently dropped in getLocalUserFilesAsPrefixMap and the
saves were never scanned or synced.

WindowsHome is the Windows user home directory (C:\users\xuser\ in Wine),
which is exactly what PathType.Root maps to. Fix by recognising
"windowshome", "%windowshome%", and "root" in PathType.from() as Root,
and adding Root to the isWindows set so it passes the save pattern filter.

* fix: recognize SteamCloudDocuments UFS root as WinMyDocuments for cloud save sync

Steam PICS can specify `root: SteamCloudDocuments` in save file patterns
(e.g. Sonic Mania, app 584400). PathType.from() did not handle this token,
causing it to fall through to PathType.None and be silently dropped during
save pattern filtering.

SteamCloudDocuments is Steam's name for the user's Documents folder, which
maps to WinMyDocuments (C:\users\xuser\Documents\) in Wine. Fix by
recognising "steamclouddocuments" and "%steamclouddocuments%" in
PathType.from() as WinMyDocuments.

* fix: normalize '.' save path to empty string to prevent broken cloud keys

Steam PICS manifests sometimes use `path: .` to mean "root of this path
type, no subdirectory" (common in Unity games). When a Windows rootoverride
also has a non-empty addpath, the dot was appended literally — producing
paths like "Thunder Lotus Games/Spiritfarer/." and uploadPath = "." —
which caused cloudPrefixToLocalPath to build a key like "%GameInstall%."
that never matches the bare "%GameInstall%" prefix the cloud API returns,
so downloaded files landed in the wrong directory.

Fix by normalising "." to "" at parse time in KeyValueUtils, consistent
with how UserFileInfo.prefix already treats cloudPath == ".". Affected
games: Spiritfarer and CrossCode.

* fix: recognize WinProgramData and SteamUserBaseStorage UFS path types, handle oslist in rootoverrides

- Add WinProgramData PathType mapping to drive_c/ProgramData/
- Alias SteamUserBaseStorage to SteamUserData in PathType.from()
- Check oslist field alongside os when filtering Windows rootoverrides

* fix: bump CURRENT_UFS_PARSE_VERSION to 2 to force re-parse of cached UFS data

Ensures existing cached SteamApp rows are re-parsed to pick up the path
normalization and oslist rootoverride fixes from this branch.

* fix: lowercase Root/ROOT_MOD aliases and add wrapped %root% form in PathType.from()

* fix: add %steamuserbasestorage% as suggested by coderabbit

* fix: perf hud fps fix for other wrappers (#1164)

* fix(hud): measure fps from render frames

* fix(hud): track fps against topmost app window

* Fix silent cloud save overwrite when sync cache is missing (#1169)

* fix: show conflict dialog when cloud sync cache is missing, regardless of change number

the old gate (localAppChangeNumber >= 0) skipped the conflict dialog
for first-time offline players whose change number is -1. this meant
local saves were silently overwritten by cloud on reconnect. remove
the gate: if cache is absent and local files exist, always treat as
conflict.

* test: cloud sync decision matrix covering all cache/CN/cloud states

12 scenarios covering: cache present/absent, local changes/none,
cloud ahead/same, preferred save location, first-time offline play.
also fixes existing download test (cloud filenames, mock params,
deprecated API).

* fix: split cache-absent conflict into upgrade vs first-offline cases

* test: revert modifications to existing tests, keep only new additions

* fixed steam intent launches, no need to check for offline mode

* Added option to toggle button hints bar (#811)

* fix crashes on migrateGSESavesToSteamUserdata (#1207)

* GOG chunk URL broken when CDN token is in query string (#1215)

* Update CONTRIBUTING.md to reduce ambiguity

* Update pull_request_template.md to reduce ambiguity

* Jb/streaming assembly utkarsh (#1219)

* feat: streaming download+assembly for Epic/GOG to reduce disk usage

replace two-phase (download all chunks → assemble all files) with a
unified loop that assembles files front-to-back as their chunks land
and deletes consumed chunks immediately. peak disk usage drops from
~2x install size to ~1x.

- StreamingAssembly: shared pure logic for chunk ordering, last-file
  tracking, readiness checks, and safe deletion decisions
- EpicDownloadManager: downloadAndAssembleEpicChunks replaces separate
  download+assembly phases for both base game and DLC
- GOGDownloadManager: downloadAndAssembleChunks with secure link
  refresh, used by both main game and dependency downloads. removed
  dead downloadChunksSimple and assembleFiles.
- 14 unit tests covering ordering, deduplication, shared chunks,
  cleanup safety, and full batch-loop simulations

* fix: run final assembly pass for zero-chunk Epic files

mirrors the GOG fix — when all files have zero chunks, the chunk loop
never executes and assembly never runs without a trailing pass.

* fix: allow all-zero-chunk Epic manifests to reach assembly

* Resolved conflicts, sped up GOG, made resuming downloads for GOG work better, made downloading UI progress smoother

* handle retries correctly for assembly for GOG

* addressed coderabbit comments

* More AI fixes

---------

Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Aligned epic downloads to GOG to make it faster from eg India (#1220)

* Aligned epic downloads to GOG to make it faster from eg India

* coderabbit comments, removed xserverscreen mistake changes

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Revert "feat: added multi-controller support (#1047)" (#1224)

This reverts commit 2334981.

* Create ROADMAP.md

* Update ROADMAP.md

* tests: Added unit tests for new key parts of Gamenative (#1143)

* Added launch dependency tests

* Added game fix registry tests

* Added preinstall step tests

* Some AI improvements + moved gamefixes tests to new types folder

* Added test to keep canonical root at the correct location (#1144)

* fix: preserve aspect-correct viewport for screen effects (#1213)

* Create pr-label-command.yml

* Recommendation page (#1235)

* Added recommendations to game page + library, added toggle to hide recommendations,

* Added review scores to recommended games

* Added date to recommended app screen like the others

* coderabit comments

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: skip spurious conflict when cache lost but local==remote (#1228)

destructive db migration wipes file_change_lists cache, making every
steam game trigger conflict on first launch post-update. check if
local state is byte-identical to remote manifest (by filename + SHA)
before declaring conflict; if so, rehydrate cache and report UpToDate
silently. also populates real timestamps on the genuine-divergence
path (was showing epoch).

test: dbCleared_localMatchesRemote_rehydratesSilently_noConflict

* chore(): openApi specs for Gog, Epic & Amazon (#1234)

* chore(): openApi specs for Gog, Epic & Amazon

* chore(): Update the epic token to look more fake.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* added toggle to disable posthog tracking (#1236)

* added toggle to disable posthog tracking

* Updated readme to include analytics info

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* refactor: Moved Proton downloading to launch deps (again, but mirroring original behaviour + tests) (#1052)

* Reapply "Refactored default Proton downloads to launch deps" (#1050)

This reverts commit 1cd84b4.

* Moved proton download to original location by moving launch deps call

* Removed deletion since previously we didnt do that either.

* Added test for new launch dep

* Added codeonwers file

* fix(): toggle showing achievements (#1251)

* fix(): toggle showing achievements

* fix(): fixed imports and state on settings. Testing now.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: mouse movement in games that use raw mouse input and clipcursor (#1084)

* refactor: register X extensions with event and error id assignment

* add writeFP3232 to XOutputStream

* feat: add XInput2 Extension and associated events

* Implement ClipCursor behavior and fix XWarpPointer to include soft margin in the window calculations

* add logging to help extension debugging

* fix: correct confinement off by one

* fix: throw BadValue if no mask as per XI2 spec

* fix: change Bitmask to hold 64 bits to be able to read XI2 masks

* More fallout game fixes (#1134)

* fix: reject config with missing components, show dialog with apply-anyway option (#801)

* fix: reject config with missing components, offer apply-anyway with defaults

* fix: pass storeMatch to force-apply path for cross-store configs

* Added kingdom hearts 3 (EPIC) gamefix (#1161)

* feat: Add Steam save import and export actions (#966)

* Add Steam save import and export actions

* fix: harden Steam save import and export

* fix: correct manifest driver id

* fix: stabilize Steam save archive roots

* fix: make Steam save root ids collision resistant

* fix: simplify Steam save transfer to userdata

* feat: add multi-root save discovery and translations

* refactor: use string resources for app option menu labels

* Workshop Update: Manual Mod Folder Dialog (#1072)

* Workshop Update: Manual Mod Folder Dialog

- Full-screen workshop manager dialog with hero image, mod list,
  select-all/deselect-all, and per-mod toggle switches
- FolderPickerDialog for manually choosing mod installation paths
  when automatic detection fails
- Improved mod path detection: binary scanning, config file parsing,
  AppData walking with fuzzy matching
- CKM (Creation Kit Module) extraction for Skyrim mods
- LZMA decompression with concurrent processing
- ZIP extraction for single-archive workshop uploads
- Magic-byte file type detection and extension fixing
- Disk space checking before downloads
- allSelected derivedStateOf optimization (removed wasteful toMap key)
- Added KNOWN_EXTENSIONS to WorkshopItem for file type validation

* Fix folder picker breadcrumb for multi-level paths

* Guard folder picker directory listing with try/catch/finally

* Rethrow CancellationException in folder picker LaunchedEffect

* feat: task manager from qam (#1121)

* feat(quick-menu): replace tools tab with task manager launcher

* feat(quick-menu): show running exe memory usage

* feat(ui): streamline quick menu task manager

* refactor: inline process user lookup in ProcessHelper

* fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)

* feat: extract XAudio DLLs from DirectX redistributables

Adds support for decompressing and installing DirectX audio components (XAudio, XACT, X3DAudio) from game cabinet files into the Wine prefix. This uses 7-Zip bindings to extract the necessary DLLs to system folders, helping resolve audio compatibility issues in games that rely on specific redistributable versions.

* refactor replaceXAudioDllsFromRedistributable to XServerScreenUtils

add other gamesource detection

* fix gameId detection

* surround gameId detection with try catch

* update use FileOutputStream for dll extraction

* add log when appDirPath detection failed

* move SevenZip init outside per file function

* ai comments

* only apply for proton 10, fix proton 9.0 compatibility

* revert SteamUtils changes

* fix directXDir detection logic

* fix directXDir detection logic

* update proton 10 logic

* fix: kill stale wine processes before launch (#1195)

* fix: kill stale wine processes before launch

* fix: wait for stale wine processes to exit

* fix: block back during prelaunch loading

* refactor: move stale wine process kill logic to ProcessHelper

* fix: GOG cloud save fetch failure handling (#1201)

* Fix GOG cloud save fetch handling

* Add GOG cloud save regression tests

* Clarify GOG cloud save fallback comment

* feat: Parallelize Steam cloud save downloads (#1226)

* Refactor Steam cloud download flow

No logic changes. Extract the per-file download body from the forEach loop in
downloadFiles into a new private downloadSingleFile function to make the
upcoming parallelisation diff easier to read.

The httpClient is moved from steamInstance.steamClient.configuration.httpClient
(pulled inline) to a parameter so the caller controls which client to use.

* Parallelize Steam cloud save downloads

Replace the sequential forEach loop in downloadFiles with a
coroutineScope { map { async { semaphore.withPermit { ... } } }.awaitAll() }
pattern, capping concurrency at PrefManager.downloadSpeed via a Semaphore.

A dedicated OkHttpClient is created per sync via Net.httpForParallelDownloads
so each parallel call gets its own Dispatcher thread pool.

filesDownloaded and bytesDownloaded are promoted from plain vars to AtomicInteger
and AtomicLong to handle concurrent updates safely.

Per-file streaming progress is added: downloadedRawBytes (incremented per
chunk via a CAS loop) drives onProgress during the download, while a separate
lastReportedPercent AtomicInteger deduplicates callbacks so only strictly
increasing percentages are emitted. The upfront indeterminate "Downloading
filename" callback is removed in favour of this.

downloadSingleFile gains httpClient, totalRawBytes, downloadedRawBytes,
lastReportedPercent, completedFiles, totalFiles, and progressMessage
parameters to support the above. The unused steamInstance parameter is
removed. copyTo now returns total bytes read and passes chunkBytes to the
progress callback.

Adds steam_cloud_sync_downloading_save_files string to all 14 locale files
for the X/Y files in-progress message.

* Fix cloud save download edge cases

Wrap the connection phase (httpClient.newCall.execute) in a try/catch for
SocketTimeoutException and IOException, returning null on failure instead of
propagating an uncaught exception.

Capture the withTimeout(responseTimeout) block as a Boolean result and return
null if the response body stream was absent. Previously a null body silently
completed without writing any file; now both the compressed and uncompressed
paths guard with ?: return@withTimeout false.

Move response.close() to a finally block so it fires on every exit path.
Remove the redundant explicit close that was on the !isSuccessful path.

Fix UserFilesDownloadResult to report rawFileSize (uncompressed bytes) instead
of fileSize (compressed bytes). The mismatch caused bytesDownloaded to
undercount for compressed files and allowed the download progress to
transiently exceed 100%.

Emit "Download complete" unconditionally after awaitAll() rather than only
when filesDownloaded == totalFiles, so a partial failure no longer stalls the
UI at the last reported percentage.

Add IOException and SocketTimeoutException catches inside the streaming try
block (distinct from the outer connection-phase catches) with a shared finally
for response.close().

* Fix cloud save download robustness issues

Close response on unsuccessful HTTP to prevent connection leaks, catch
TimeoutCancellationException so one download timeout doesn't cancel all
parallel downloads, treat short reads as failures and clean up partial
files, and only report "Download complete" when all files succeeded.

* fix: address Steam cloud review feedback

Handle Steam cloud metadata fetch failures per file without swallowing coroutine cancellation, so the reviewed exception path no longer cancels sibling downloads while structured cancellation still works correctly.

Also tear down the per-sync Steam cloud download client after the batch completes to release dispatcher threads and pooled connections, while keeping failed-download file handling aligned with current master behavior.

* fix: use stored installPath in GOGManager.deleteGame to prevent uninstall failures (#1255)

* added new turnip drivers to manifest (#1263)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* chore(): add new box64 to GN. (#1262)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Revert "fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)" (#1266)

This reverts commit 3a4cb3d.

* Added some changes for rockstar launcher (#1274)

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Updated manifest to include new protons (#1275)

* Updated manifest to include new protons

* Fixed manifest values

* fixed proton 10 x86-64 id

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* Added THIRD_PARTY_NOTICES

* feat/refactor: Improve GOGDownloadManager download efficiency (#1277)

* refactor: Improve GOGDownloadManager download efficiency

1. Adopt Flow queuing concept from JavaSteam
2. add DownloadSpeedConfig to be used later for other store enhancement
3. update kotlinx-coroutines-core version to match JavaSteam version using
4. Update NetworkUtils httpForParallelDownloads for timeout and http protocol config

* ai comments

* add retry logic when same pendingChunks appear 10 times in a row

* fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding (#1269)

* Reapply "fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)" (#1266)

This reverts commit 93793fa.

* fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding

Replaces the 7-Zip JBinding library with a Wine-based batch script that uses the native cabarc utility to extract DirectX DLLs. This removes the dependency on JitPack and the external 7-Zip library while maintaining the fix for Proton 10.

* ai comments

* ai comments

* ai comments, refactor to XAudioUtils

* guard batchCommand with BATCH_SUCCESS_CHECK

* move guard code location after all extraction

* remove useless comment

* add splash text when extracting dlls

* fix(workshop): force standard Steam UGC path for Tale of Immortal (#1260)

- Add AppID 1468810 (Tale of Immortal) to `forceStandardAppIds` to ensure workshop items are handled via the standard ISteamUGC path.

* Move Touchscreen Mode toggle to in-game sidebar (#1249)

Moved Touchscreen Mode toggle from Edit Container > Controller section to the in-game sidebar, right below the existing Edit On-screen Controller line. Added gear icon for gesture settings that appears when touchscreen mode is active.

* fix: late release single tap in touchscreen mode to fix clicking in some games (#1212)

* fix: late release single tap in touchscreen mode to fix clicking in some games

* fix: immediately release click before a new single tap

* clear delayedPress state after execution

* flush pending single tap release on handleTsDown

* Update README.md

* feat(): Silly draft for ideation of EOS.

* Added sidecar for EOS, made downloading EOS launch dependency, Deliver at all Costs working

* addressed AI comments

* fix: gog download pause / resume handling logic

* fix unit test

* revert downloadChunk logic on md5 checking before downloading file

* Made layouts for appscreen and libraryscreen cutout/notch aware

* Merge pull request #1300 from utkarshdalal/unpack-more

made unpack files more aggressive

* fix: resolve cloud save path for Danganronpa 2 (app 413420) (#1297)

Danganronpa 2 stores saves in WinMyDocuments/My Games/Danganronpa2/ via
a Windows rootoverride, but GameNative was placing downloaded cloud saves
in the game install directory instead, so the game never found them.

Two bugs fixed:

1. KeyValueUtils: treat PICS path '/' as empty (same as '.')
   A lone forward-slash means 'root of this path type' with no subdir.
   Keeping the literal '/' caused uploadPath='/' which put a trailing
   slash on the cloudPrefixToLocalPath map key ('%GameInstall%/') while
   the lookup trimmed it ('%GameInstall%'), causing a miss. With the fix,
   uploadPath='' and path='My Games/Danganronpa2' (no trailing slash).
   Also bumps CURRENT_UFS_PARSE_VERSION 2->3 to force cache refresh.

2. SteamAutoCloud getFullFilePath: consult cloudPrefixToLocalPath when
   Steam embeds the placeholder in the filename (prefix=[],
   filename='%GameInstall%savedata.vfs'). The previous early-return
   hardcoded the destination as the game install dir, bypassing all
   rootoverride remapping. Now checks cloudPrefixToLocalPath['%GameInstall%']
   first so the file lands in WinMyDocuments/My Games/Danganronpa2/.

Also adds .trimEnd('/') to cloudKey construction in cloudPrefixToLocalPath
so map keys are always slash-free (matching the existing lookup behaviour).

Tests added:
- KeyValueUtilsTest: danganronpa2SlashPathWithWindowsRootOverrideIsNormalizedToEmpty
- SteamAutoCloudTest: downloadWithEmbeddedGameInstallPrefixUsesRootoverrideLocalPath
- keyvalues/Danganronpa 2 Goodbye Despair.txt: PICS reference documentation

* fix/tabbar-scrolling-clip (#1293)

* fix: copy a new dll generated with proton 11 (#1287)

* fix: open download details fullscreen (#1270)

* fix: repair steam save export (#1265)

* feat: move Disable Mouse Input to in-game Quick Menu overlay (#1267)

* feat: move Disable Mouse Input to in-game Quick Menu overlay

Removes the Disable Mouse Input toggle from the container settings
Controller tab and adds it to the in-game Quick Menu, where it can
be toggled during gameplay without leaving the game. State is
persisted to the container config on toggle.

* fix: address code review feedback

- Remove unconditional setCursorVisible call; cursor state is managed
  by existing pointer-availability logic and should not be overridden
  when a physical mouse may be present
- Key isDisableMouseInput state to container.id to prevent stale state
  if container changes

* fix: restore cursor visibility on mouse input toggle

Show cursor when re-enabling mouse input (unless touchscreen mode is
also active), hide it when disabling. Required for touch-as-mouse
users who have no physical pointer device.

* fix: use mouse icon and pink accent for Disable Mouse quick menu item

* fix: join prealloc job before polling so zero-chunk files are created before return (#1308)

* fix: use consistent purple accent color for all quick menu items (#1311)

* Stop overwriting wine prefix when switching between containers of dif… (#1310)

* Stop overwriting wine prefix when switching between containers of different variants/archs, only extract xaudio dlls once

* do reinstall of mono and preinstallsteps on wine version change (not arch), fixed bugs with wincomponents, audio driver, and startup selection being overwritten on wine version change

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>

* fix: soften boot splash hero backdrop

* fix: resolve boot splash hero image off main thread

* made booting splash image greyscale background instead of having inconsistent color that doesn't gel with GN

---------

Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Luboš V. <tridosm@gmail.com>
Co-authored-by: UnbelievableFlavour <bart.zaalberg@shift2.nl>
Co-authored-by: Joshua Tam <297250+joshuatam@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>
Co-authored-by: CatPotatos <catarinaleal123@gmail.com>
Co-authored-by: AnikethanVA <82267223+AnikethanVA@users.noreply.github.com>
Co-authored-by: Dan Brooke <mail@danbrooke.net>
Co-authored-by: Misazam <60115666+Misazam@users.noreply.github.com>
Co-authored-by: bllendev <113651082+bllendev@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: André Vitor <90573731+AndreVto@users.noreply.github.com>
Co-authored-by: Nightwalker743 <Hillardjoseph43@gmail.com>
Co-authored-by: linkq <mrlinkq@hotmail.com>
Co-authored-by: Daniel Joyce <danielalexanderjoyce@gmail.com>
Co-authored-by: tlt21 <travistryba@gmail.com>
Co-authored-by: Almond <88301593+sdkahal@users.noreply.github.com>
Co-authored-by: Ben Pearson <ben@buriza.co.uk>

* Removing Comments like wanted in Codereview

---------

Co-authored-by: Utkarsh Dalal <dalal.utkarsh@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Luboš V. <tridosm@gmail.com>
Co-authored-by: UnbelievableFlavour <bart.zaalberg@shift2.nl>
Co-authored-by: Joshua Tam <297250+joshuatam@users.noreply.github.com>
Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>
Co-authored-by: CatPotatos <catarinaleal123@gmail.com>
Co-authored-by: AnikethanVA <82267223+AnikethanVA@users.noreply.github.com>
Co-authored-by: Dan Brooke <mail@danbrooke.net>
Co-authored-by: Misazam <60115666+Misazam@users.noreply.github.com>
Co-authored-by: bllendev <113651082+bllendev@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: André Vitor <90573731+AndreVto@users.noreply.github.com>
Co-authored-by: Nightwalker743 <Hillardjoseph43@gmail.com>
Co-authored-by: linkq <mrlinkq@hotmail.com>
Co-authored-by: Daniel Joyce <danielalexanderjoyce@gmail.com>
Co-authored-by: tlt21 <travistryba@gmail.com>
Co-authored-by: Almond <88301593+sdkahal@users.noreply.github.com>
Co-authored-by: Ben Pearson <ben@buriza.co.uk>
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.

Disable IME extract UI for Compose TextFields

2 participants