1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
// Hook the SteamVR Hotbar
import { ErrorBoundary, Patch, afterPatch, findInReactTree, findModuleExport, getReactRoot } from '@decky/ui';
import { ReactNode, createElement } from 'react';
import Logger from '../logger';
declare global {
interface Window {
__VR_DASHBAORD_BAR_HOOK_INSTANCE: any;
}
}
// TODO kinda dumb
enum VRDashboardBarButtonID {
Decky = 999,
}
export interface VRDashboardBarButton {
id: VRDashboardBarButtonID | number;
active?: boolean;
children?: ReactNode;
tooltip?: ReactNode;
popupContents?: ReactNode;
onActivate?: () => void;
refBarPopupHandle?: any; // TODO idk
onPopupVisibilityChanged?: (visible: boolean) => void;
}
class VRDashboardBarHook extends Logger {
rightSideButtons: VRDashboardBarButton[] = [];
private rendererdRightSideButtons: ReactNode[] = [];
private barPatch?: Patch;
private barButton?: any;
constructor() {
super('VRDashboardBarHook');
this.log('Initialized');
window.__VR_DASHBAORD_BAR_HOOK_INSTANCE?.deinit?.();
window.__VR_DASHBAORD_BAR_HOOK_INSTANCE = this;
}
init() {
this.barButton = findModuleExport((e) => e?.render?.toString()?.includes('VRDashboardBarSmallButtonActive'));
const barComponent = findModuleExport((e) => e?.type?.toString()?.includes('vrLocation=="dashboard-bar"'));
this.barPatch = afterPatch(barComponent, 'type', (_, ret) => {
const nodes = findInReactTree(ret, (x) => x?.length > 6);
if (!window?.securitystore?.IsLockScreenActive?.()) nodes.splice(4, 0, this.rendererdRightSideButtons);
return ret;
});
// Patch already rendered bar
const root = getReactRoot(document.getElementById('root') as any);
const barNode =
root &&
findInReactTree(
root,
(n: any) => n.elementType == barComponent || (barComponent != null && n.elementType == barComponent),
); // need elementType, because case where type is actually mobx wrapper
if (barNode) {
// Only affects this fiber node so we don't need to unpatch here
barNode.type = barNode.elementType.type;
if (barNode?.alternate) {
barNode.alternate.type = barNode.type;
}
}
}
deinit() {
this.barPatch?.unpatch();
}
addRightSideButton(button: VRDashboardBarButton) {
this.debug('Adding RSB', button.id, 'to render array');
this.rightSideButtons.push(button);
this.renderRightSideButtons();
}
removeRightSideButton(id: number) {
this.debug('Removing RSB', id);
this.rightSideButtons = this.rightSideButtons.filter((button) => button.id !== id);
this.renderRightSideButtons();
}
renderRightSideButtons() {
const arr = [];
for (const button of this.rightSideButtons) {
arr.push(
createElement(this.barButton, {
active: button.active,
children: <ErrorBoundary>{button.children}</ErrorBoundary>,
tooltip: button.tooltip,
popupContents: <ErrorBoundary>{button.popupContents}</ErrorBoundary>,
onActivate: button.onActivate,
refBarPopupHandle: button.refBarPopupHandle,
onPopupVisibilityChanged: button.onPopupVisibilityChanged,
}),
);
}
this.rendererdRightSideButtons = arr;
}
}
export default VRDashboardBarHook;
|