diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-08 16:25:11 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-08 16:25:11 -0400 |
| commit | 8352ee74e8a437c1f4a4dadb75d63049f45b164b (patch) | |
| tree | bd59691eb01f5fbf4b5a6aede5ae4d0295e6643d /src/components | |
| parent | b3749ecc4b094a46d2ab9f638ee810f70840f67a (diff) | |
| download | decky-lsfg-vk-8352ee74e8a437c1f4a4dadb75d63049f45b164b.tar.gz decky-lsfg-vk-8352ee74e8a437c1f4a4dadb75d63049f45b164b.zip | |
add back workarounds sections, scope out of now playing
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/ConfigurationTab.tsx | 2 | ||||
| -rw-r--r-- | src/components/GameConfigurationControls.tsx | 9 | ||||
| -rw-r--r-- | src/components/NowPlayingTab.tsx | 2 | ||||
| -rw-r--r-- | src/components/WorkaroundsSection.tsx | 190 | ||||
| -rw-r--r-- | src/components/index.ts | 1 |
5 files changed, 203 insertions, 1 deletions
diff --git a/src/components/ConfigurationTab.tsx b/src/components/ConfigurationTab.tsx index 6ba8f5d..2bb0f26 100644 --- a/src/components/ConfigurationTab.tsx +++ b/src/components/ConfigurationTab.tsx @@ -149,6 +149,8 @@ export function ConfigurationTab({ onConfigChange={onConfigChange} autoFocusFpsMultiplier={focusFpsMultiplier} onFpsMultiplierFocused={clearFpsFocusRequest} + showWorkarounds + workaroundTarget={selectedTarget || undefined} /> )} {selectedTarget?.configured && ( diff --git a/src/components/GameConfigurationControls.tsx b/src/components/GameConfigurationControls.tsx index 34b82c5..58ccd02 100644 --- a/src/components/GameConfigurationControls.tsx +++ b/src/components/GameConfigurationControls.tsx @@ -1,12 +1,16 @@ import { ConfigurationData } from "../config/configSchema"; +import type { GameTarget } from "../hooks/useGameConfiguration"; import { ConfigurationSection } from "./ConfigurationSection"; import { FpsMultiplierControl } from "./FpsMultiplierControl"; +import { WorkaroundsSection } from "./WorkaroundsSection"; interface Props { config: ConfigurationData; onConfigChange: (fieldName: keyof ConfigurationData, value: boolean | number | string | string[]) => Promise<void>; autoFocusFpsMultiplier?: boolean; onFpsMultiplierFocused?: () => void; + showWorkarounds?: boolean; + workaroundTarget?: Pick<GameTarget, "appid" | "nonSteam">; } export function GameConfigurationControls({ @@ -14,6 +18,8 @@ export function GameConfigurationControls({ onConfigChange, autoFocusFpsMultiplier, onFpsMultiplierFocused, + showWorkarounds = false, + workaroundTarget, }: Props) { return ( <> @@ -24,6 +30,9 @@ export function GameConfigurationControls({ onAutoFocus={onFpsMultiplierFocused} /> <ConfigurationSection config={config} onConfigChange={onConfigChange} /> + {showWorkarounds && workaroundTarget && ( + <WorkaroundsSection appId={workaroundTarget.appid} nonSteam={workaroundTarget.nonSteam} /> + )} </> ); } diff --git a/src/components/NowPlayingTab.tsx b/src/components/NowPlayingTab.tsx index 2ec12ca..188c56a 100644 --- a/src/components/NowPlayingTab.tsx +++ b/src/components/NowPlayingTab.tsx @@ -19,7 +19,7 @@ export function NowPlayingTab({ game, config, onConfigChange }: Props) { /> </PanelSectionRow> </PanelSection> - <GameConfigurationControls config={config} onConfigChange={onConfigChange} /> + <GameConfigurationControls config={config} onConfigChange={onConfigChange} showWorkarounds={false} /> </Focusable> ); } diff --git a/src/components/WorkaroundsSection.tsx b/src/components/WorkaroundsSection.tsx new file mode 100644 index 0000000..e990784 --- /dev/null +++ b/src/components/WorkaroundsSection.tsx @@ -0,0 +1,190 @@ +import { ButtonItem, Field, PanelSectionRow, SliderField, ToggleField } from "@decky/ui"; +import { useEffect, useState } from "react"; +import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; +import { usePerAppWorkarounds } from "../hooks/usePerAppWorkarounds"; +import t from "../i18n/i18n"; +import type { WorkaroundField } from "../utils/steamLaunchOptions"; + +interface WorkaroundsSectionProps { + appId: string; + nonSteam: boolean; +} + +const WORKAROUNDS_COLLAPSED_KEY = "lsfg-workarounds-collapsed"; +type ToggleWorkaroundField = Exclude<WorkaroundField, "dxvkFrameRate">; + +const TOGGLE_ROWS: readonly { + field: ToggleWorkaroundField; + labelKey: string; + label: string; + descriptionKey: string; + description: string; +}[] = [ + { + field: "disableGamescopeWsi", + labelKey: "CONFIG_DISABLE_GAMESCOPE_WSI", + label: "Disable Gamescope WSI", + descriptionKey: "CONFIG_DISABLE_GAMESCOPE_WSI_DESC", + description: "Adds ENABLE_GAMESCOPE_WSI=0 without changing HDR. Requires game restart to apply.", + }, + { + field: "disableSteamdeckMode", + labelKey: "CONFIG_DISABLE_STEAMDECK_MODE", + label: "Disable Steam Deck Mode", + descriptionKey: "CONFIG_DISABLE_STEAMDECK_MODE_DESC", + description: "Disables a game-specific Steam Deck compatibility switch. Requires game restart to apply.", + }, + { + field: "disableVkbasalt", + labelKey: "CONFIG_DISABLE_VKBASALT", + label: "Disable vkBasalt", + descriptionKey: "CONFIG_DISABLE_VKBASALT_DESC", + description: "Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)", + }, + { + field: "enableZink", + labelKey: "CONFIG_ENABLE_ZINK", + label: "Force Zink for OpenGL Games", + descriptionKey: "CONFIG_ENABLE_ZINK_DESC", + description: "Uses Mesa's Zink OpenGL-to-Vulkan driver. May cause crashes or freezes with some games. Requires game restart to apply.", + }, +]; + +function usePersistentCollapsed() { + const [collapsed, setCollapsed] = useState(() => { + try { + const saved = localStorage.getItem(WORKAROUNDS_COLLAPSED_KEY); + return saved !== null ? JSON.parse(saved) === true : true; + } catch { + return true; + } + }); + + useEffect(() => { + try { + localStorage.setItem(WORKAROUNDS_COLLAPSED_KEY, JSON.stringify(collapsed)); + } catch { + // Persisting the view preference is optional. + } + }, [collapsed]); + + return [collapsed, () => setCollapsed((value) => !value)] as const; +} + +export function WorkaroundsSection({ appId, nonSteam }: WorkaroundsSectionProps) { + const [collapsed, toggleCollapsed] = usePersistentCollapsed(); + const { status, snapshot, refresh, update, error } = usePerAppWorkarounds(appId, nonSteam); + const state = snapshot?.parsed.state; + const issues = snapshot?.parsed.issues || []; + const controlsDisabled = status !== "ready" || state === undefined; + const [fpsValue, setFpsValue] = useState<number | null>(null); + const effectiveFpsValue = fpsValue ?? state?.dxvkFrameRate ?? 0; + const fpsLabel = effectiveFpsValue > 0 + ? `${effectiveFpsValue} FPS` + : t("CONFIG_BASE_FPS_CAP_OFF", "Off"); + + useEffect(() => { + setFpsValue(state?.dxvkFrameRate ?? null); + }, [state?.dxvkFrameRate, status]); + + return ( + <> + <style> + {` + .LSFG_WorkaroundsCollapseButton_Container > div > div > div > button, + .LSFG_WorkaroundsCollapseButton_Container > div > div > div > div > button { + height: 24px !important; + padding: 0 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + } + `} + </style> + <PanelSectionRow> + <div + style={{ + fontSize: "14px", + fontWeight: "bold", + marginTop: "8px", + marginBottom: "6px", + borderBottom: "1px solid rgba(255, 255, 255, 0.2)", + paddingBottom: "3px", + color: "white", + }} + > + {t("CONFIG_WORKAROUNDS_TITLE", "Workarounds")} + </div> + </PanelSectionRow> + <PanelSectionRow> + <div className="LSFG_WorkaroundsCollapseButton_Container" style={{ marginTop: "-2px", marginBottom: "4px" }}> + <ButtonItem + layout="below" + bottomSeparator={collapsed ? "standard" : "none"} + onClick={toggleCollapsed} + > + {collapsed ? <RiArrowDownSFill /> : <RiArrowUpSFill />} + </ButtonItem> + </div> + </PanelSectionRow> + + {!collapsed && ( + <> + {status === "loading" && ( + <PanelSectionRow> + <Field label="Reading launch options..." /> + </PanelSectionRow> + )} + {status === "error" && ( + <> + <PanelSectionRow> + <Field + label="Launch options unavailable" + description={error || "Steam did not provide readable launch options."} + /> + </PanelSectionRow> + <PanelSectionRow> + <ButtonItem layout="below" onClick={() => void refresh()}>Retry</ButtonItem> + </PanelSectionRow> + </> + )} + {status === "ready" && issues.length > 0 && ( + <PanelSectionRow> + <Field + label="Launch options need attention" + description={`${issues.join(" ")} Adjusting a workaround will normalize its managed values.`} + /> + </PanelSectionRow> + )} + + <PanelSectionRow> + <SliderField + label={`${t("CONFIG_BASE_FPS_CAP", "Base FPS Cap")} (${fpsLabel})`} + description={t("CONFIG_BASE_FPS_CAP_DESC", "Base cap for DXVK-backed games before frame generation; 0 disables. Requires game restart to apply.")} + value={effectiveFpsValue} + min={0} + max={60} + step={1} + onChange={(value) => { + setFpsValue(value); + void update("dxvkFrameRate", value); + }} + disabled={controlsDisabled} + /> + </PanelSectionRow> + {TOGGLE_ROWS.map((row) => ( + <PanelSectionRow key={row.field}> + <ToggleField + label={t(row.labelKey, row.label)} + description={t(row.descriptionKey, row.description)} + checked={Boolean(state?.[row.field])} + onChange={(value) => void update(row.field, value)} + disabled={controlsDisabled} + /> + </PanelSectionRow> + ))} + </> + )} + </> + ); +} diff --git a/src/components/index.ts b/src/components/index.ts index 424a360..37a8edb 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -10,3 +10,4 @@ export { FlatpaksTab } from "./FlatpaksTab"; export { GameConfigurationSelector } from "./GameConfigurationSelector"; export { GameConfigurationControls } from "./GameConfigurationControls"; export { NowPlayingTab } from "./NowPlayingTab"; +export { WorkaroundsSection } from "./WorkaroundsSection"; |
