From bace5143d28c42ffcc83509b7fcdf02b6cae6934 Mon Sep 17 00:00:00 2001 From: TrainDoctor Date: Sun, 30 Oct 2022 10:32:05 -0700 Subject: Merge Tabs and Injection Fixes, bring back native Valve toaster (#238) * Bring back component patch-based tabshook * better injection point * finally fix dumb loading error * fix QAM injection breaking after lock * shut up typescript * fix lock screen focusing issues * Bring back the Valve toaster! * Add support for stable steamos * fix focus bug on lock screen but actually * oops: remove extra console log * shut up typescript again * better fix for lockscreen bug * better probably * actually fix focus issues (WTF) Co-authored-by: AAGaming --- frontend/src/tabs-hook.tsx | 154 +++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 60 deletions(-) (limited to 'frontend/src/tabs-hook.tsx') diff --git a/frontend/src/tabs-hook.tsx b/frontend/src/tabs-hook.tsx index 5929b8a0..c7790f7e 100644 --- a/frontend/src/tabs-hook.tsx +++ b/frontend/src/tabs-hook.tsx @@ -1,22 +1,18 @@ -import { QuickAccessTab, quickAccessMenuClasses, sleep } from 'decky-frontend-lib'; +// TabsHook for versions after the Desktop merge +import { Patch, QuickAccessTab, afterPatch, findInReactTree, findModule, sleep } from 'decky-frontend-lib'; +import { memo } from 'react'; import { QuickAccessVisibleStateProvider } from './components/QuickAccessVisibleState'; import Logger from './logger'; +import { findSP } from './utils/windows'; declare global { interface Window { __TABS_HOOK_INSTANCE: any; - } - interface Array { - __filter: any; + securitystore: any; } } -const isTabsArray = (tabs: any) => { - const length = tabs.length; - return length >= 7 && tabs[length - 1]?.tab; -}; - interface Tab { id: QuickAccessTab | number; title: any; @@ -27,7 +23,9 @@ interface Tab { class TabsHook extends Logger { // private keys = 7; tabs: Tab[] = []; - private oFilter: (...args: any[]) => any; + private qAMRoot?: any; + private qamPatch?: Patch; + private unsubscribeSecurity?: () => void; constructor() { super('TabsHook'); @@ -35,65 +33,90 @@ class TabsHook extends Logger { this.log('Initialized'); window.__TABS_HOOK_INSTANCE?.deinit?.(); window.__TABS_HOOK_INSTANCE = this; + } - const self = this; - const oFilter = (this.oFilter = Array.prototype.filter); - Array.prototype.filter = function patchedFilter(...args: any[]) { - if (isTabsArray(this)) { - self.render(this); + init() { + const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current; + let qAMRoot: any; + const findQAMRoot = (currentNode: any, iters: number): any => { + if (iters >= 55) { + // currently 45 + return null; + } + if ( + typeof currentNode?.memoizedProps?.visible == 'boolean' && + currentNode?.type?.toString()?.includes('QuickAccessMenuBrowserView') + ) { + this.log(`QAM root was found in ${iters} recursion cycles`); + return currentNode; } - // @ts-ignore - return oFilter.call(this, ...args); + if (currentNode.child) { + let node = findQAMRoot(currentNode.child, iters + 1); + if (node !== null) return node; + } + if (currentNode.sibling) { + let node = findQAMRoot(currentNode.sibling, iters + 1); + if (node !== null) return node; + } + return null; }; - - if (document.title != 'SP') - try { - const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current; - let qAMRoot: any; - async function findQAMRoot(currentNode: any, iters: number): Promise { - if (iters >= 60) { - // currently 44 - return null; - } - currentNode = currentNode?.child; - if ( - currentNode?.memoizedProps?.className && - currentNode?.memoizedProps?.className.startsWith(quickAccessMenuClasses.ViewPlaceholder) - ) { - self.log(`QAM root was found in ${iters} recursion cycles`); - return currentNode; + (async () => { + qAMRoot = findQAMRoot(tree, 0); + while (!qAMRoot) { + this.error( + 'Failed to find QAM root node, reattempting in 5 seconds. A developer may need to increase the recursion limit.', + ); + await sleep(5000); + qAMRoot = findQAMRoot(tree, 0); + } + this.qAMRoot = qAMRoot; + let patchedInnerQAM: any; + this.qamPatch = afterPatch(qAMRoot.return, 'type', (_: any, ret: any) => { + try { + if (!qAMRoot?.child) { + qAMRoot = findQAMRoot(tree, 0); + this.qAMRoot = qAMRoot; } - if (!currentNode) return null; - if (currentNode.sibling) { - let node = await findQAMRoot(currentNode.sibling, iters + 1); - if (node !== null) return node; + if (qAMRoot?.child && !qAMRoot?.child?.type?.decky) { + afterPatch(qAMRoot.child, 'type', (_: any, ret: any) => { + try { + const qamTabsRenderer = findInReactTree(ret, (x) => x?.props?.onFocusNavDeactivated); + if (patchedInnerQAM) { + qamTabsRenderer.type = patchedInnerQAM; + } else { + afterPatch(qamTabsRenderer, 'type', (innerArgs: any, ret: any) => { + const tabs = findInReactTree(ret, (x) => x?.props?.tabs); + this.render(tabs.props.tabs, innerArgs[0].visible); + return ret; + }); + patchedInnerQAM = qamTabsRenderer.type; + } + } catch (e) { + this.error('Error patching QAM inner', e); + } + return ret; + }); + qAMRoot.child.type.decky = true; + qAMRoot.child.alternate.type = qAMRoot.child.type; } - return await findQAMRoot(currentNode, iters + 1); + } catch (e) { + this.error('Error patching QAM', e); } - (async () => { - qAMRoot = await findQAMRoot(tree, 0); - while (!qAMRoot) { - this.error( - 'Failed to find QAM root node, reattempting in 5 seconds. A developer may need to increase the recursion limit.', - ); - await sleep(5000); - qAMRoot = await findQAMRoot(tree, 0); - } - while (!qAMRoot?.stateNode?.forceUpdate) { - qAMRoot = qAMRoot.return; - } - qAMRoot.stateNode.shouldComponentUpdate = () => true; - qAMRoot.stateNode.forceUpdate(); - delete qAMRoot.stateNode.shouldComponentUpdate; - })(); - } catch (e) { - this.log('Failed to rerender QAM', e); + return ret; + }); + + if (qAMRoot.return.alternate) { + qAMRoot.return.alternate.type = qAMRoot.return.type; } + this.log('Finished initial injection'); + })(); } deinit() { - Array.prototype.filter = this.oFilter; + this.qamPatch?.unpatch(); + this.qAMRoot.return.alternate.type = this.qAMRoot.return.type; + this.unsubscribeSecurity?.(); } add(tab: Tab) { @@ -106,14 +129,25 @@ class TabsHook extends Logger { this.tabs = this.tabs.filter((tab) => tab.id !== id); } - render(existingTabs: any[]) { + render(existingTabs: any[], visible: boolean) { + let deckyTabAmount = existingTabs.reduce((prev: any, cur: any) => (cur.decky ? prev + 1 : prev), 0); + if (deckyTabAmount == this.tabs.length) { + for (let tab of existingTabs) { + if (tab?.decky) tab.panel.props.setter[0](visible); + } + return; + } for (const { title, icon, content, id } of this.tabs) { existingTabs.push({ key: id, title, tab: icon, decky: true, - panel: {content}, + panel: ( + + {content} + + ), }); } } -- cgit v1.2.3