├── .python-version ├── src ├── vite-env.d.ts ├── IFEO │ ├── index.module.css │ ├── IoPriority.tsx │ ├── PagePriority.tsx │ ├── CpuPriority.tsx │ ├── AddProcess.tsx │ └── index.tsx ├── DISPLAY_INFO │ ├── index.module.css │ └── index.tsx ├── App.module.css ├── PCI_IRQs │ ├── index.module.css │ ├── MSISupported.tsx │ ├── MessageNumberLimit.tsx │ ├── DevicePriority.tsx │ ├── DevicePolicy.tsx │ ├── AssignmentSetOverride.tsx │ └── index.tsx ├── COMPATIBILITY_OPTIONS │ ├── index.module.css │ ├── AddPath.tsx │ ├── Table.tsx │ ├── index.tsx │ └── Options.tsx ├── POWER_SETTINGS │ ├── index.module.css │ ├── PossibleValues.tsx │ ├── Values.tsx │ ├── SelectOption.tsx │ ├── NumberOption.tsx │ └── index.tsx ├── main.tsx ├── shared.ts ├── SCHEDULING │ ├── AffinityMask.tsx │ └── index.tsx └── App.tsx ├── assets └── icon.ico ├── images ├── IFEO.png ├── PCI_IRQs.png ├── Display_Info.png ├── Scheduling.png ├── Power_Settings.png └── Compatibility_Options.png ├── tsconfig.json ├── .github ├── dependabot.yaml └── workflows │ └── release.yaml ├── pyproject.toml ├── vite.config.ts ├── index.html ├── .gitignore ├── postcss.config.cjs ├── README.md ├── .vscode └── launch.json ├── tsconfig.node.json ├── tsconfig.app.json ├── eslint.config.js ├── py ├── SCHEDULING │ └── __init__.py ├── COMPATIBILITY_OPTIONS │ └── __init__.py ├── IFEO │ └── __init__.py ├── utils │ └── __init__.py ├── PCI_IRQs │ └── __init__.py ├── POWER_SETTINGS │ └── __init__.py └── DISPLAY_INFO │ └── __init__.py ├── package.json ├── main.pyw └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/IFEO/index.module.css: -------------------------------------------------------------------------------- 1 | .addProcess { 2 | flex-grow: 1; 3 | } 4 | -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/assets/icon.ico -------------------------------------------------------------------------------- /images/IFEO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/IFEO.png -------------------------------------------------------------------------------- /src/DISPLAY_INFO/index.module.css: -------------------------------------------------------------------------------- 1 | .finePrint { 2 | font-size: 0.5rem; 3 | color: #676767; 4 | } 5 | -------------------------------------------------------------------------------- /images/PCI_IRQs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/PCI_IRQs.png -------------------------------------------------------------------------------- /images/Display_Info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/Display_Info.png -------------------------------------------------------------------------------- /images/Scheduling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/Scheduling.png -------------------------------------------------------------------------------- /src/App.module.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | z-index: 100; 6 | } 7 | -------------------------------------------------------------------------------- /images/Power_Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/Power_Settings.png -------------------------------------------------------------------------------- /images/Compatibility_Options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoringBoredom/Windows-MultiTool/HEAD/images/Compatibility_Options.png -------------------------------------------------------------------------------- /src/PCI_IRQs/index.module.css: -------------------------------------------------------------------------------- 1 | .finePrint { 2 | font-size: 0.7rem; 3 | color: #676767; 4 | } 5 | .policyWidth { 6 | min-width: 18rem; 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "windows-multitool" 3 | version = "0.0.0" 4 | requires-python = ">=3.13" 5 | dependencies = [ 6 | "cryptography>=44.0.2", 7 | "pyinstaller>=6.12.0", 8 | "pywebview>=5.4", 9 | ] 10 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | build: { outDir: "gui" }, 8 | }); 9 | -------------------------------------------------------------------------------- /src/COMPATIBILITY_OPTIONS/index.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | text-align: center; 3 | font-weight: bold; 4 | background-color: var(--mantine-color-dark-5); 5 | } 6 | .centerCheckbox :global(.mantine-Checkbox-body) { 7 | justify-content: center; 8 | } 9 | .addPath { 10 | flex-grow: 1; 11 | } 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Windows MultiTool 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/index.module.css: -------------------------------------------------------------------------------- 1 | .powerPlanSelection { 2 | position: fixed; 3 | bottom: 0; 4 | right: 0; 5 | z-index: 100; 6 | background-color: var(--mantine-color-dark-8); 7 | border-left: 1px solid var(--mantine-color-dark-3); 8 | border-top: 1px solid var(--mantine-color-dark-3); 9 | } 10 | 11 | .valuesWidth { 12 | min-width: 12rem; 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | gui 15 | build 16 | *.spec 17 | __pycache__ 18 | 19 | # Editor directories and files 20 | !.vscode/extensions.json 21 | .idea 22 | .DS_Store 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-preset-mantine": {}, 4 | "postcss-simple-vars": { 5 | variables: { 6 | "mantine-breakpoint-xs": "36em", 7 | "mantine-breakpoint-sm": "48em", 8 | "mantine-breakpoint-md": "62em", 9 | "mantine-breakpoint-lg": "75em", 10 | "mantine-breakpoint-xl": "88em", 11 | }, 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | - WebView2 4 | 5 | # Features 6 | 7 | - ### PCI Interrupt Management 8 | ![](images/PCI_IRQs.png) 9 | - ### Display Information 10 | ![](images/Display_Info.png) 11 | - ### IFEO Priority Management 12 | ![](images/IFEO.png) 13 | - ### Scheduling Configuration 14 | ![](images/Scheduling.png) 15 | - ### Power Settings 16 | ![](images/Power_Settings.png) 17 | - ### App Compatibility Settings 18 | ![](images/Compatibility_Options.png) 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python Debugger", 9 | "type": "debugpy", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/main.pyw", 12 | "console": "integratedTerminal" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | 5 | import { AppShell, MantineProvider } from "@mantine/core"; 6 | import "@mantine/core/styles.css"; 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 9 | createRoot(document.getElementById("root")!).render( 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 | ); 24 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noUncheckedSideEffectImports": true 24 | }, 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/PossibleValues.tsx: -------------------------------------------------------------------------------- 1 | import { Divider, Tooltip } from "@mantine/core"; 2 | import { Fragment } from "react"; 3 | import type { Setting } from "."; 4 | 5 | export default function PossibleValues({ setting }: { setting: Setting }) { 6 | const { options } = setting; 7 | 8 | if (options) { 9 | return options.map((option, index) => ( 10 | 11 | 12 |
{option.name}
13 |
14 | {index < options.length - 1 && } 15 |
16 | )); 17 | } else if (setting.range) { 18 | return ( 19 | <> 20 |
Min: {setting.range.min}
21 | 22 |
Max: {setting.range.max}
23 | 24 |
Step: {setting.range.increment}
25 | 26 |
Unit: {setting.range.unit}
27 | 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/shared.ts: -------------------------------------------------------------------------------- 1 | export const enum REGISTRY_DATA_TYPES { 2 | REG_NONE = 0, 3 | REG_SZ = 1, 4 | REG_EXPAND_SZ = 2, 5 | REG_BINARY = 3, 6 | REG_DWORD = 4, 7 | REG_DWORD_BIG_ENDIAN = 5, 8 | REG_LINK = 6, 9 | REG_MULTI_SZ = 7, 10 | REG_RESOURCE_LIST = 8, 11 | REG_FULL_RESOURCE_DESCRIPTOR = 9, 12 | REG_RESOURCE_REQUIREMENTS_LIST = 10, 13 | REG_QWORD = 11, 14 | } 15 | 16 | export function formatRegValue(name: string, value: number | null) { 17 | if (value !== null) { 18 | return `"${name}"=dword:${value.toString(16).padStart(8, "0")}\n`; 19 | } else { 20 | return `"${name}"=-\n`; 21 | } 22 | } 23 | 24 | export function formatBitMask(value: number | null, isReg: boolean) { 25 | if (value !== null) { 26 | const hexString = value.toString(16).padStart(16, "0"); 27 | 28 | const bytes: string[] = []; 29 | for (let i = 0; i < hexString.length; i += 2) { 30 | bytes.push(hexString.slice(i, i + 2)); 31 | } 32 | 33 | return isReg 34 | ? `"hex:${bytes.reverse().join(",")}"` 35 | : bytes.reverse().join(""); 36 | } else { 37 | return "-"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/Values.tsx: -------------------------------------------------------------------------------- 1 | import type { Setting } from "."; 2 | import NumberOption from "./NumberOption"; 3 | import SelectOption from "./SelectOption"; 4 | 5 | export default function Values({ 6 | setting, 7 | schemeGuid, 8 | subgroupGuid, 9 | }: { 10 | setting: Setting; 11 | schemeGuid: string; 12 | subgroupGuid: string; 13 | }) { 14 | if (setting.options) { 15 | return ( 16 | <> 17 | 23 | 29 | 30 | ); 31 | } 32 | 33 | return ( 34 | <> 35 | 41 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/SelectOption.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 2 | import { NativeSelect } from "@mantine/core"; 3 | import { useState } from "react"; 4 | import type { Setting } from "."; 5 | 6 | export default function SelectOption({ 7 | setting, 8 | type, 9 | schemeGuid, 10 | subgroupGuid, 11 | }: { 12 | setting: Setting; 13 | type: "ac" | "dc"; 14 | schemeGuid: string; 15 | subgroupGuid: string; 16 | }) { 17 | const [value, setValue] = useState(setting.options![setting[type]].index); 18 | 19 | return ( 20 | ({ 25 | label: option.name, 26 | value: option.index.toString(), 27 | }))} 28 | onChange={(ev) => { 29 | const value = parseInt(ev.currentTarget.value); 30 | 31 | window.pywebview.api 32 | .writeValueIndex( 33 | schemeGuid, 34 | subgroupGuid, 35 | setting.guid, 36 | value, 37 | type === "ac" 38 | ) 39 | .then(() => { 40 | setValue(value); 41 | }) 42 | .catch((error: unknown) => { 43 | alert(error instanceof Error ? error.toString() : error); 44 | }); 45 | }} 46 | /> 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from "@eslint/js"; 2 | import globals from "globals"; 3 | import reactHooks from "eslint-plugin-react-hooks"; 4 | import reactRefresh from "eslint-plugin-react-refresh"; 5 | import tseslint from "typescript-eslint"; 6 | import reactX from "eslint-plugin-react-x"; 7 | import reactDom from "eslint-plugin-react-dom"; 8 | 9 | export default tseslint.config( 10 | { ignores: ["dist"] }, 11 | { 12 | extends: [ 13 | js.configs.recommended, 14 | ...tseslint.configs.strictTypeChecked, 15 | ...tseslint.configs.stylisticTypeChecked, 16 | ], 17 | files: ["**/*.{ts,tsx}"], 18 | languageOptions: { 19 | ecmaVersion: 2020, 20 | globals: globals.browser, 21 | parserOptions: { 22 | project: ["./tsconfig.node.json", "./tsconfig.app.json"], 23 | tsconfigRootDir: import.meta.dirname, 24 | }, 25 | }, 26 | plugins: { 27 | "react-hooks": reactHooks, 28 | "react-refresh": reactRefresh, 29 | "react-x": reactX, 30 | "react-dom": reactDom, 31 | }, 32 | rules: { 33 | ...reactHooks.configs.recommended.rules, 34 | "react-refresh/only-export-components": [ 35 | "warn", 36 | { allowConstantExport: true }, 37 | ], 38 | ...reactX.configs["recommended-typescript"].rules, 39 | ...reactDom.configs.recommended.rules, 40 | }, 41 | } 42 | ); 43 | -------------------------------------------------------------------------------- /py/SCHEDULING/__init__.py: -------------------------------------------------------------------------------- 1 | from os import cpu_count 2 | from typing import Final 3 | from winreg import ( 4 | HKEY_LOCAL_MACHINE, 5 | KEY_READ, 6 | KEY_WOW64_64KEY, 7 | OpenKeyEx, 8 | QueryValueEx, 9 | ) 10 | 11 | KERNEL_PATH: Final = r"SYSTEM\CurrentControlSet\Control\Session Manager\kernel" 12 | 13 | 14 | def get_scheduling_info(): 15 | with OpenKeyEx( 16 | HKEY_LOCAL_MACHINE, KERNEL_PATH, 0, KEY_READ | KEY_WOW64_64KEY 17 | ) as base_key: 18 | try: 19 | reserved_cpu_sets = int.from_bytes( 20 | QueryValueEx(base_key, "ReservedCpuSets")[0], "little" 21 | ) 22 | except FileNotFoundError: 23 | reserved_cpu_sets = None 24 | 25 | try: 26 | with OpenKeyEx( 27 | HKEY_LOCAL_MACHINE, 28 | KERNEL_PATH + r"KGroups\00", 29 | 0, 30 | KEY_READ | KEY_WOW64_64KEY, 31 | ) as kgroup_key: 32 | small_processor_mask = int.from_bytes( 33 | QueryValueEx(kgroup_key, "SmallProcessorMask")[0], "little" 34 | ) 35 | except FileNotFoundError: 36 | small_processor_mask = None 37 | 38 | return { 39 | "ReservedCpuSets": reserved_cpu_sets, 40 | "SmallProcessorMask": small_processor_mask, 41 | "cpu": {"cpus": cpu_count()}, 42 | } 43 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/NumberOption.tsx: -------------------------------------------------------------------------------- 1 | import { NumberInput } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import type { Setting } from "."; 4 | 5 | export default function NumberOption({ 6 | setting, 7 | type, 8 | schemeGuid, 9 | subgroupGuid, 10 | }: { 11 | setting: Setting; 12 | type: "ac" | "dc"; 13 | schemeGuid: string; 14 | subgroupGuid: string; 15 | }) { 16 | const [value, setValue] = useState(setting[type]); 17 | const { range } = setting; 18 | 19 | return ( 20 | { 25 | if (typeof value === "number") { 26 | window.pywebview.api 27 | .writeValueIndex( 28 | schemeGuid, 29 | subgroupGuid, 30 | setting.guid, 31 | value, 32 | type === "ac" 33 | ) 34 | .then(() => { 35 | setValue(value); 36 | }) 37 | .catch((error: unknown) => { 38 | alert(error instanceof Error ? error.toString() : error); 39 | }); 40 | } else { 41 | setValue(value); 42 | } 43 | }} 44 | min={range !== null ? range.min : 0} 45 | max={range !== null ? range.max : undefined} 46 | step={range !== null ? range.increment : undefined} 47 | clampBehavior="strict" 48 | allowNegative={false} 49 | allowDecimal={false} 50 | stepHoldDelay={250} 51 | stepHoldInterval={1} 52 | /> 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | run-name: Create release 3 | 4 | on: 5 | workflow_dispatch: 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | build: 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version-file: ".python-version" 22 | 23 | - name: Set up Node 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: 22 27 | cache: "npm" 28 | 29 | - name: Install uv 30 | uses: astral-sh/setup-uv@v6 31 | with: 32 | activate-environment: true 33 | 34 | - name: Install dependencies 35 | shell: cmd 36 | run: npm run init 37 | 38 | - name: Build 39 | shell: cmd 40 | run: npm run build-binary 41 | 42 | - name: Get version from package.json 43 | id: version 44 | shell: bash 45 | run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT 46 | 47 | - name: Upload artifact 48 | uses: actions/upload-artifact@v4 49 | with: 50 | path: dist\WindowsMultiTool.exe 51 | 52 | - name: Create release 53 | uses: ncipollo/release-action@v1 54 | with: 55 | tag: ${{ steps.version.outputs.VERSION }} 56 | name: ${{ steps.version.outputs.VERSION }} 57 | artifacts: dist\WindowsMultiTool.exe 58 | generateReleaseNotes: true 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "windows-multitool", 3 | "private": true, 4 | "version": "0.4.3", 5 | "type": "module", 6 | "repo": "BoringBoredom/Windows-MultiTool", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc -b && vite build", 10 | "lint": "eslint .", 11 | "preview": "vite preview", 12 | "init": "npm ci && uv sync", 13 | "clean": "(if exist dist rmdir /S /Q dist) && (if exist build rmdir /S /Q build) && (if exist gui rmdir /S /Q gui) && (if exist \"WindowsMultiTool.spec\" del /q \"WindowsMultiTool.spec\")", 14 | "pyinstaller": "uv run pyinstaller main.pyw --name \"WindowsMultiTool\" --add-data=\"gui:gui\" --onefile --noconsole --icon=\"assets\\icon.ico\" --uac-admin", 15 | "build-binary": "npm run clean && npm run build && npm run pyinstaller" 16 | }, 17 | "dependencies": { 18 | "@mantine/core": "^7.17.3", 19 | "@mantine/hooks": "^7.17.3", 20 | "@tabler/icons-react": "3.17.0", 21 | "react": "^19.0.0", 22 | "react-dom": "^19.0.0" 23 | }, 24 | "devDependencies": { 25 | "@eslint/js": "^9.21.0", 26 | "@types/react": "^19.0.10", 27 | "@types/react-dom": "^19.0.4", 28 | "@vitejs/plugin-react": "^4.3.4", 29 | "eslint": "^9.21.0", 30 | "eslint-plugin-react-dom": "^1.40.0", 31 | "eslint-plugin-react-hooks": "^5.1.0", 32 | "eslint-plugin-react-refresh": "^0.4.19", 33 | "eslint-plugin-react-x": "^1.40.0", 34 | "globals": "^16", 35 | "postcss": "^8.5.3", 36 | "postcss-preset-mantine": "^1.17.0", 37 | "postcss-simple-vars": "^7.0.1", 38 | "typescript": "~5.7.2", 39 | "typescript-eslint": "^8.24.1", 40 | "vite": "^6.2.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PCI_IRQs/MSISupported.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import { MSI_PATH, type SystemInfo } from "."; 4 | import { REGISTRY_DATA_TYPES } from "../shared"; 5 | 6 | export default function MSISupportedField({ 7 | device, 8 | }: { 9 | device: SystemInfo["devices"][number]; 10 | }) { 11 | const [value, setValue] = useState( 12 | device.MSISupported?.toString() ?? null 13 | ); 14 | 15 | return ( 16 | { 24 | if (value === null) { 25 | window.pywebview.api 26 | .deleteRegistryValue( 27 | "HKLM", 28 | data.Path + "\\PerfOptions", 29 | "IoPriority" 30 | ) 31 | .then(() => { 32 | setValue(value); 33 | }) 34 | .catch((error: unknown) => { 35 | alert(error instanceof Error ? error.toString() : error); 36 | }); 37 | } else { 38 | window.pywebview.api 39 | .writeRegistryValue( 40 | "HKLM", 41 | data.Path + "\\PerfOptions", 42 | "IoPriority", 43 | REGISTRY_DATA_TYPES.REG_DWORD, 44 | parseInt(value) 45 | ) 46 | .then(() => { 47 | setValue(value); 48 | }) 49 | .catch((error: unknown) => { 50 | alert(error instanceof Error ? error.toString() : error); 51 | }); 52 | } 53 | }} 54 | /> 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/IFEO/PagePriority.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import type { IfeoDataValue } from "."; 4 | import { REGISTRY_DATA_TYPES } from "../shared"; 5 | 6 | export default function PagePriorityField({ data }: { data: IfeoDataValue }) { 7 | const [value, setValue] = useState( 8 | data.PagePriority?.toString() ?? null 9 | ); 10 | 11 | return ( 12 | { 24 | if (value === null) { 25 | window.pywebview.api 26 | .deleteRegistryValue( 27 | "HKLM", 28 | data.Path + "\\PerfOptions", 29 | "CpuPriorityClass" 30 | ) 31 | .then(() => { 32 | setValue(value); 33 | }) 34 | .catch((error: unknown) => { 35 | alert(error instanceof Error ? error.toString() : error); 36 | }); 37 | } else { 38 | window.pywebview.api 39 | .writeRegistryValue( 40 | "HKLM", 41 | data.Path + "\\PerfOptions", 42 | "CpuPriorityClass", 43 | REGISTRY_DATA_TYPES.REG_DWORD, 44 | parseInt(value) 45 | ) 46 | .then(() => { 47 | setValue(value); 48 | }) 49 | .catch((error: unknown) => { 50 | alert(error instanceof Error ? error.toString() : error); 51 | }); 52 | } 53 | }} 54 | /> 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/PCI_IRQs/DevicePriority.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import { AFFINITY_PATH, type SystemInfo } from "."; 4 | import { REGISTRY_DATA_TYPES } from "../shared"; 5 | 6 | export default function DevicePriorityField({ 7 | device, 8 | }: { 9 | device: SystemInfo["devices"][number]; 10 | }) { 11 | const [value, setValue] = useState( 12 | device.DevicePriority?.toString() ?? null 13 | ); 14 | 15 | return ( 16 | { 36 | if (value === null) { 37 | window.pywebview.api 38 | .deleteRegistryValue( 39 | "HKLM", 40 | device.Path + AFFINITY_PATH, 41 | "DevicePolicy" 42 | ) 43 | .then(() => { 44 | setValue(value); 45 | }) 46 | .catch((error: unknown) => { 47 | alert(error instanceof Error ? error.toString() : error); 48 | }); 49 | } else { 50 | window.pywebview.api 51 | .writeRegistryValue( 52 | "HKLM", 53 | device.Path + AFFINITY_PATH, 54 | "DevicePolicy", 55 | REGISTRY_DATA_TYPES.REG_DWORD, 56 | parseInt(value) 57 | ) 58 | .then(() => { 59 | setValue(value); 60 | }) 61 | .catch((error: unknown) => { 62 | alert(error instanceof Error ? error.toString() : error); 63 | }); 64 | } 65 | }} 66 | /> 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /py/COMPATIBILITY_OPTIONS/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | from winreg import ( 3 | HKEY_CURRENT_USER, 4 | HKEY_LOCAL_MACHINE, 5 | KEY_READ, 6 | KEY_WOW64_64KEY, 7 | REG_SZ, 8 | EnumValue, 9 | OpenKeyEx, 10 | QueryInfoKey, 11 | ) 12 | 13 | CompatibilityOptions = list[str | None] 14 | HiveData = dict[str, CompatibilityOptions] 15 | 16 | COMPAT_PATH: Final = ( 17 | r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" 18 | ) 19 | 20 | options_map: list[set[str]] = [ 21 | {"~"}, 22 | {"DISABLEDXMAXIMIZEDWINDOWEDMODE"}, 23 | {"RUNASADMIN"}, 24 | {"640X480"}, 25 | {"PERPROCESSSYSTEMDPIFORCEOFF", "PERPROCESSSYSTEMDPIFORCEON"}, 26 | {"HIGHDPIAWARE", "DPIUNAWARE", "GDIDPISCALING DPIUNAWARE"}, 27 | {"256COLOR"}, 28 | {"16BITCOLOR"}, 29 | {"TRANSFORMLEGACYCOLORMANAGED"}, 30 | { 31 | "WIN95", 32 | "WIN98", 33 | "WINXPSP2", 34 | "WINXPSP3", 35 | "VISTARTM", 36 | "VISTASP1", 37 | "VISTASP2", 38 | "WIN7RTM", 39 | "WIN8RTM", 40 | }, 41 | ] 42 | 43 | 44 | def read_hive(hive: int): 45 | entries: HiveData = {} 46 | 47 | try: 48 | with OpenKeyEx(hive, COMPAT_PATH, 0, KEY_READ | KEY_WOW64_64KEY) as key: 49 | for i in range(QueryInfoKey(key)[1]): 50 | name, value, type = EnumValue(key, i) 51 | 52 | if isinstance(value, str) and type == REG_SZ: 53 | flags = value.split(" ") 54 | parsed_options: CompatibilityOptions = [None] * len(options_map) 55 | 56 | for flag in flags: 57 | for index, valid_options in enumerate(options_map): 58 | if flag in valid_options: 59 | parsed_options[index] = flag 60 | break 61 | 62 | entries[name] = parsed_options 63 | except FileNotFoundError: 64 | pass 65 | 66 | return entries 67 | 68 | 69 | def get_compatibility_options(): 70 | return { 71 | "HKLM": read_hive(HKEY_LOCAL_MACHINE), 72 | "HKCU": read_hive(HKEY_CURRENT_USER), 73 | } 74 | -------------------------------------------------------------------------------- /py/IFEO/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Final, TypedDict 2 | from winreg import ( 3 | HKEY_LOCAL_MACHINE, 4 | KEY_READ, 5 | KEY_WOW64_64KEY, 6 | EnumKey, 7 | OpenKeyEx, 8 | QueryInfoKey, 9 | QueryValueEx, 10 | ) 11 | 12 | BASE_PATH: Final = ( 13 | r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" 14 | ) 15 | 16 | 17 | class IFEO(TypedDict): 18 | Path: str 19 | CpuPriorityClass: int | None 20 | IoPriority: int | None 21 | PagePriority: int | None 22 | 23 | 24 | def get_ifeo_data(): 25 | ifeo: dict[str, IFEO] = {} 26 | 27 | with OpenKeyEx( 28 | HKEY_LOCAL_MACHINE, BASE_PATH, 0, KEY_READ | KEY_WOW64_64KEY 29 | ) as base_key: 30 | for i in range(QueryInfoKey(base_key)[0]): 31 | process_name = EnumKey(base_key, i) 32 | subkey_path = f"{BASE_PATH}\\{process_name}" 33 | 34 | try: 35 | with OpenKeyEx( 36 | HKEY_LOCAL_MACHINE, 37 | subkey_path + r"\PerfOptions", 38 | 0, 39 | KEY_READ | KEY_WOW64_64KEY, 40 | ) as perf_options_key: 41 | try: 42 | cpu_priority_class = QueryValueEx( 43 | perf_options_key, "CpuPriorityClass" 44 | )[0] 45 | except FileNotFoundError: 46 | cpu_priority_class = None 47 | 48 | try: 49 | io_priority = QueryValueEx(perf_options_key, "IoPriority")[0] 50 | except FileNotFoundError: 51 | io_priority = None 52 | 53 | try: 54 | page_priority = QueryValueEx(perf_options_key, "PagePriority")[ 55 | 0 56 | ] 57 | except FileNotFoundError: 58 | page_priority = None 59 | 60 | ifeo[process_name] = { 61 | "Path": subkey_path, 62 | "CpuPriorityClass": cpu_priority_class, 63 | "IoPriority": io_priority, 64 | "PagePriority": page_priority, 65 | } 66 | except FileNotFoundError: 67 | pass 68 | 69 | return ifeo 70 | -------------------------------------------------------------------------------- /src/IFEO/AddProcess.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Group, TextInput } from "@mantine/core"; 2 | import { IconPlus } from "@tabler/icons-react"; 3 | import { useState } from "react"; 4 | import { IFEO_PATH, type IfeoData } from "."; 5 | import s from "./index.module.css"; 6 | 7 | export default function AddNewProcess({ 8 | ifeoData, 9 | setIfeoData, 10 | }: { 11 | ifeoData: IfeoData; 12 | setIfeoData: React.Dispatch>; 13 | }) { 14 | const [name, setName] = useState(""); 15 | 16 | return ( 17 | 18 | { 23 | if (!name.trim()) { 24 | alert("Enter a process name."); 25 | return; 26 | } 27 | 28 | const lowerCaseName = name.toLowerCase(); 29 | const normalizedName = lowerCaseName.endsWith(".exe") 30 | ? name 31 | : name + ".exe"; 32 | 33 | if (ifeoData.get(normalizedName)) { 34 | alert(`${normalizedName} already exists.`); 35 | return; 36 | } 37 | 38 | const path = `${IFEO_PATH}\\${normalizedName}`; 39 | 40 | window.pywebview.api 41 | .createRegistryKey(path + "\\PerfOptions") 42 | .then(() => { 43 | setIfeoData((prev) => { 44 | if (!prev) { 45 | return prev; 46 | } 47 | 48 | const newData = new Map(prev); 49 | newData.set(normalizedName, { 50 | Path: path, 51 | CpuPriorityClass: null, 52 | IoPriority: null, 53 | PagePriority: null, 54 | }); 55 | 56 | return newData; 57 | }); 58 | 59 | setName(""); 60 | }) 61 | .catch((error: unknown) => { 62 | alert(error instanceof Error ? error.toString() : error); 63 | }); 64 | }} 65 | > 66 | 67 | 68 | 69 | { 75 | setName(ev.currentTarget.value); 76 | }} 77 | /> 78 | 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /py/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from ctypes import Structure, c_ubyte, c_ulong, c_ushort 2 | from typing import Literal 3 | from uuid import UUID 4 | from winreg import ( 5 | HKEY_CURRENT_USER, 6 | HKEY_LOCAL_MACHINE, 7 | KEY_READ, 8 | KEY_WOW64_64KEY, 9 | KEY_WRITE, 10 | REG_BINARY, 11 | CreateKeyEx, 12 | DeleteKeyEx, 13 | DeleteValue, 14 | OpenKeyEx, 15 | SetValueEx, 16 | ) 17 | 18 | 19 | class GUID(Structure): 20 | _fields_ = [ 21 | ("Data1", c_ulong), 22 | ("Data2", c_ushort), 23 | ("Data3", c_ushort), 24 | ("Data4", c_ubyte * 8), 25 | ] 26 | 27 | def __init__(self, uuid_string: str | None = None): 28 | if uuid_string: 29 | uuid_obj = UUID(uuid_string) 30 | self.Data1 = uuid_obj.time_low 31 | self.Data2 = uuid_obj.time_mid 32 | self.Data3 = uuid_obj.time_hi_version 33 | for i in range(8): 34 | self.Data4[i] = uuid_obj.bytes[8 + i] 35 | super().__init__() 36 | 37 | def __str__(self): 38 | return "{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}".format( 39 | self.Data1, 40 | self.Data2, 41 | self.Data3, 42 | *self.Data4, 43 | ) 44 | 45 | 46 | def write_registry_value( 47 | hkey_str: Literal["HKLM", "HKCU"], path: str, name: str, type: int, value: int | str 48 | ): 49 | hkey = HKEY_LOCAL_MACHINE if hkey_str == "HKLM" else HKEY_CURRENT_USER 50 | 51 | with CreateKeyEx(hkey, path, 0, KEY_WRITE | KEY_WOW64_64KEY) as key: 52 | if type == REG_BINARY and isinstance(value, str): 53 | SetValueEx(key, name, 0, type, int(value, 2).to_bytes(8, "little")) 54 | else: 55 | SetValueEx(key, name, 0, type, value) 56 | 57 | 58 | def delete_registry_value(hkey_str: Literal["HKLM", "HKCU"], path: str, value: str): 59 | hkey = HKEY_LOCAL_MACHINE if hkey_str == "HKLM" else HKEY_CURRENT_USER 60 | 61 | with OpenKeyEx(hkey, path, 0, KEY_WRITE | KEY_WOW64_64KEY) as key: 62 | DeleteValue(key, value) 63 | 64 | 65 | def delete_registry_key(path: str, key: str): 66 | with OpenKeyEx( 67 | HKEY_LOCAL_MACHINE, path, 0, KEY_WRITE | KEY_WOW64_64KEY 68 | ) as base_key: 69 | DeleteKeyEx(base_key, key) 70 | 71 | 72 | def create_registry_key(path: str): 73 | with CreateKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ | KEY_WOW64_64KEY): 74 | pass 75 | -------------------------------------------------------------------------------- /src/COMPATIBILITY_OPTIONS/AddPath.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Group, TextInput } from "@mantine/core"; 2 | import { IconPlus } from "@tabler/icons-react"; 3 | import { useState } from "react"; 4 | import { 5 | COMPAT_PATH, 6 | type CompatibilityOptions, 7 | type CompatibilityOptionsData, 8 | } from "."; 9 | import { REGISTRY_DATA_TYPES } from "../shared"; 10 | import s from "./index.module.css"; 11 | 12 | export default function AddPath({ 13 | hiveStr, 14 | compatibilityOptionsData, 15 | setCompatibilityOptionsData, 16 | }: { 17 | hiveStr: "HKLM" | "HKCU"; 18 | compatibilityOptionsData: CompatibilityOptionsData; 19 | setCompatibilityOptionsData: React.Dispatch< 20 | React.SetStateAction 21 | >; 22 | }) { 23 | const [name, setName] = useState(""); 24 | 25 | return ( 26 | 27 | { 32 | if (!name.trim()) { 33 | alert("Enter a process path."); 34 | return; 35 | } 36 | 37 | if (compatibilityOptionsData[hiveStr].get(name)) { 38 | alert(`"${name}" already exists.`); 39 | return; 40 | } 41 | 42 | window.pywebview.api 43 | .writeRegistryValue( 44 | hiveStr, 45 | COMPAT_PATH, 46 | name, 47 | REGISTRY_DATA_TYPES.REG_SZ, 48 | "" 49 | ) 50 | .then(() => { 51 | setCompatibilityOptionsData((prev) => { 52 | if (!prev) { 53 | return prev; 54 | } 55 | 56 | const newData = { ...prev }; 57 | newData[hiveStr] = new Map(prev[hiveStr]); 58 | newData[hiveStr].set( 59 | name, 60 | Array(10).fill(null) as CompatibilityOptions 61 | ); 62 | 63 | return newData; 64 | }); 65 | 66 | setName(""); 67 | }) 68 | .catch((error: unknown) => { 69 | alert(error instanceof Error ? error.toString() : error); 70 | }); 71 | }} 72 | > 73 | 74 | 75 | 76 | { 82 | setName(ev.currentTarget.value); 83 | }} 84 | /> 85 | 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /src/COMPATIBILITY_OPTIONS/Table.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Group, Table } from "@mantine/core"; 2 | import { IconMinus } from "@tabler/icons-react"; 3 | import { COMPAT_PATH, type CompatibilityOptionsData } from "."; 4 | import AddPath from "./AddPath"; 5 | import s from "./index.module.css"; 6 | import Options from "./Options"; 7 | 8 | export default function TableComponent({ 9 | compatibilityOptionsData, 10 | setCompatibilityOptionsData, 11 | hiveStr, 12 | }: { 13 | compatibilityOptionsData: CompatibilityOptionsData; 14 | setCompatibilityOptionsData: React.Dispatch< 15 | React.SetStateAction 16 | >; 17 | hiveStr: "HKLM" | "HKCU"; 18 | }) { 19 | return ( 20 | 21 | 22 | 23 | {hiveStr === "HKLM" ? "All Users" : "Current User"} 24 | 25 | 26 | 27 | {[...compatibilityOptionsData[hiveStr].entries()] 28 | .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) 29 | .map(([name, compatibilityOptions]) => ( 30 | 31 | 32 | 33 | { 38 | window.pywebview.api 39 | .deleteRegistryValue(hiveStr, COMPAT_PATH, name) 40 | .then(() => { 41 | setCompatibilityOptionsData((prev) => { 42 | if (!prev) { 43 | return prev; 44 | } 45 | 46 | const newData = { ...prev }; 47 | newData[hiveStr] = new Map(prev[hiveStr]); 48 | newData[hiveStr].delete(name); 49 | 50 | return newData; 51 | }); 52 | }) 53 | .catch((error: unknown) => { 54 | alert( 55 | error instanceof Error ? error.toString() : error 56 | ); 57 | }); 58 | }} 59 | > 60 | 61 | 62 | {name} 63 | 64 | 65 | 66 | 71 | 72 | ))} 73 | 74 | 75 | 80 | 81 | 82 | 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /src/SCHEDULING/AffinityMask.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Chip, Group } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import { REGISTRY_DATA_TYPES } from "../shared"; 4 | 5 | export default function AffinityMaskField({ 6 | cpus, 7 | path, 8 | name, 9 | value, 10 | }: { 11 | cpus: number; 12 | path: string; 13 | name: string; 14 | value: number | null; 15 | }) { 16 | const [activeCpus, setActiveCpus] = useState( 17 | value !== null 18 | ? Array.from({ length: cpus }, (_, index) => 19 | value & (1 << index) ? index.toString() : null 20 | ).filter((index) => index !== null) 21 | : [] 22 | ); 23 | 24 | return ( 25 |
26 |

{name}

27 | { 31 | if (value.length === 0) { 32 | window.pywebview.api 33 | .deleteRegistryValue("HKLM", path, name) 34 | .then(() => { 35 | setActiveCpus(value.sort((a, b) => parseInt(a) - parseInt(b))); 36 | }) 37 | .catch((error: unknown) => { 38 | alert(error instanceof Error ? error.toString() : error); 39 | }); 40 | } else { 41 | const binaryString = Array.from({ length: cpus }, (_, index) => 42 | value.includes(index.toString()) ? "1" : "0" 43 | ) 44 | .reverse() 45 | .join(""); 46 | 47 | window.pywebview.api 48 | .writeRegistryValue( 49 | "HKLM", 50 | path, 51 | name, 52 | REGISTRY_DATA_TYPES.REG_BINARY, 53 | binaryString 54 | ) 55 | .then(() => { 56 | setActiveCpus(value.sort((a, b) => parseInt(a) - parseInt(b))); 57 | }) 58 | .catch((error: unknown) => { 59 | alert(error instanceof Error ? error.toString() : error); 60 | }); 61 | } 62 | }} 63 | > 64 | 65 | {Array.from({ length: cpus }, (_, index) => ( 66 | 67 | {index} 68 | 69 | ))} 70 | 71 | 72 | 73 |
74 | 75 | 76 | 105 | 106 | 121 | 122 |
123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /src/SCHEDULING/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Center, Menu, Stack } from "@mantine/core"; 2 | import { IconChevronDown } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import s from "../App.module.css"; 5 | import { formatBitMask } from "../shared"; 6 | import AffinityMaskField from "./AffinityMask"; 7 | 8 | export const KERNEL_PATH = 9 | "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel"; 10 | 11 | export interface SchedulingInfo { 12 | ReservedCpuSets: number | null; 13 | SmallProcessorMask: number | null; 14 | cpu: { cpus: number }; 15 | } 16 | 17 | function getExportData(schedulingInfo: SchedulingInfo, isReg: boolean) { 18 | const { ReservedCpuSets, SmallProcessorMask } = schedulingInfo; 19 | 20 | let content = ""; 21 | 22 | if (isReg) { 23 | content = 24 | "Windows Registry Editor Version 5.00\n\n" + 25 | `[HKEY_LOCAL_MACHINE\\${KERNEL_PATH}]\n` + 26 | `"ReservedCpuSets"=${formatBitMask(ReservedCpuSets, true)}\n\n` + 27 | `[HKEY_LOCAL_MACHINE\\${KERNEL_PATH}\\KGroups\\00]\n` + 28 | `"SmallProcessorMask"=${formatBitMask(SmallProcessorMask, true)}`; 29 | } else { 30 | if (ReservedCpuSets !== null) { 31 | content += `reg add "HKEY_LOCAL_MACHINE\\${KERNEL_PATH}" /v ReservedCpuSets /t REG_BINARY /d ${formatBitMask( 32 | ReservedCpuSets, 33 | false 34 | )} /f\n`; 35 | } else { 36 | content += `reg delete "HKEY_LOCAL_MACHINE\\${KERNEL_PATH}" /v ReservedCpuSets /f\n`; 37 | } 38 | 39 | if (SmallProcessorMask !== null) { 40 | content += `reg add "HKEY_LOCAL_MACHINE\\${KERNEL_PATH}\\KGroups\\00" /v SmallProcessorMask /t REG_BINARY /d ${formatBitMask( 41 | SmallProcessorMask, 42 | false 43 | )} /f\n`; 44 | } else { 45 | content += `reg delete "HKEY_LOCAL_MACHINE\\${KERNEL_PATH}\\KGroups\\00" /v SmallProcessorMask /f\n`; 46 | } 47 | } 48 | 49 | return content; 50 | } 51 | 52 | export default function SCHEDULING() { 53 | const [schedulingInfo, setSchedulingInfo] = useState(); 54 | 55 | useEffect(() => { 56 | window.pywebview.api 57 | .getSchedulingInfo() 58 | .then((data) => { 59 | setSchedulingInfo(data); 60 | }) 61 | .catch((error: unknown) => { 62 | alert(error instanceof Error ? error.toString() : error); 63 | }); 64 | }, []); 65 | 66 | if (!schedulingInfo) { 67 | return; 68 | } 69 | 70 | return ( 71 | <> 72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | EXPORT 82 | { 84 | window.pywebview.api 85 | .saveFile( 86 | "scheduling.reg", 87 | ["Registry File (*.reg)"], 88 | getExportData(schedulingInfo, true) 89 | ) 90 | .catch((error: unknown) => { 91 | alert(error instanceof Error ? error.toString() : error); 92 | }); 93 | }} 94 | > 95 | .reg 96 | 97 | 98 | { 100 | window.pywebview.api 101 | .saveFile( 102 | "scheduling.bat", 103 | ["Batch File (*.bat)"], 104 | getExportData(schedulingInfo, false) 105 | ) 106 | .catch((error: unknown) => { 107 | alert(error instanceof Error ? error.toString() : error); 108 | }); 109 | }} 110 | > 111 | .bat 112 | 113 | 114 | 115 |
116 | 117 |
118 | 119 | 125 | 126 | 132 | 133 |
134 | 135 | ); 136 | } 137 | -------------------------------------------------------------------------------- /src/PCI_IRQs/AssignmentSetOverride.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Chip, Group, Modal } from "@mantine/core"; 2 | import { useDisclosure } from "@mantine/hooks"; 3 | import { useState } from "react"; 4 | import { AFFINITY_PATH, type SystemInfo } from "."; 5 | import { REGISTRY_DATA_TYPES } from "../shared"; 6 | 7 | export default function AssignmentSetOverrideField({ 8 | device, 9 | cpus, 10 | }: { 11 | device: SystemInfo["devices"][number]; 12 | cpus: number; 13 | }) { 14 | const [opened, { open, close }] = useDisclosure(false); 15 | const [activeCpus, setActiveCpus] = useState( 16 | device.AssignmentSetOverride !== null 17 | ? Array.from({ length: cpus }, (_, index) => 18 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 19 | device.AssignmentSetOverride! & (1 << index) ? index.toString() : null 20 | ).filter((index) => index !== null) 21 | : [] 22 | ); 23 | 24 | return ( 25 | <> 26 | 27 | { 31 | if (value.length === 0) { 32 | window.pywebview.api 33 | .deleteRegistryValue( 34 | "HKLM", 35 | device.Path + AFFINITY_PATH, 36 | "AssignmentSetOverride" 37 | ) 38 | .then(() => { 39 | setActiveCpus( 40 | value.sort((a, b) => parseInt(a) - parseInt(b)) 41 | ); 42 | }) 43 | .catch((error: unknown) => { 44 | alert(error instanceof Error ? error.toString() : error); 45 | }); 46 | } else { 47 | const binaryString = Array.from({ length: cpus }, (_, index) => 48 | value.includes(index.toString()) ? "1" : "0" 49 | ) 50 | .reverse() 51 | .join(""); 52 | 53 | window.pywebview.api 54 | .writeRegistryValue( 55 | "HKLM", 56 | device.Path + AFFINITY_PATH, 57 | "AssignmentSetOverride", 58 | REGISTRY_DATA_TYPES.REG_BINARY, 59 | binaryString 60 | ) 61 | .then(() => { 62 | setActiveCpus( 63 | value.sort((a, b) => parseInt(a) - parseInt(b)) 64 | ); 65 | }) 66 | .catch((error: unknown) => { 67 | alert(error instanceof Error ? error.toString() : error); 68 | }); 69 | } 70 | }} 71 | > 72 | 73 | {Array.from({ length: cpus }, (_, index) => ( 74 | 75 | {index} 76 | 77 | ))} 78 | 79 | 80 | 81 |
82 | 83 | 84 | 113 | 114 | 133 | 134 |
135 | 136 | 139 | 140 | ); 141 | } 142 | -------------------------------------------------------------------------------- /main.pyw: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from os import path 3 | from traceback import format_exc 4 | from typing import Callable, Literal, ParamSpec, TypeVar 5 | 6 | from webview import SAVE_DIALOG, create_window, start 7 | 8 | P = ParamSpec("P") 9 | R = TypeVar("R") 10 | 11 | 12 | def handle_api_errors(func: Callable[P, R]) -> Callable[P, R]: 13 | @wraps(func) 14 | def wrapper(*args: P.args, **kwargs: P.kwargs): 15 | try: 16 | return func(*args, **kwargs) 17 | except Exception: 18 | raise Exception(format_exc()) 19 | 20 | return wrapper 21 | 22 | 23 | class Api: 24 | @handle_api_errors 25 | def getSystemInfo(self): 26 | from py.PCI_IRQs import get_system_info 27 | 28 | return get_system_info() 29 | 30 | @handle_api_errors 31 | def getDisplayInfo(self): 32 | from py.DISPLAY_INFO import get_display_info 33 | 34 | return get_display_info() 35 | 36 | @handle_api_errors 37 | def writeRegistryValue( 38 | self, 39 | hkey_str: Literal["HKLM", "HKCU"], 40 | path: str, 41 | name: str, 42 | type: int, 43 | value: int | str, 44 | ): 45 | from py.utils import write_registry_value 46 | 47 | write_registry_value(hkey_str, path, name, type, value) 48 | 49 | @handle_api_errors 50 | def deleteRegistryValue( 51 | self, hkey_str: Literal["HKLM", "HKCU"], path: str, value: str 52 | ): 53 | from py.utils import delete_registry_value 54 | 55 | delete_registry_value(hkey_str, path, value) 56 | 57 | @handle_api_errors 58 | def deleteRegistryKey(self, path: str, key: str): 59 | from py.utils import delete_registry_key 60 | 61 | delete_registry_key(path, key) 62 | 63 | @handle_api_errors 64 | def createRegistryKey(self, path: str): 65 | from py.utils import create_registry_key 66 | 67 | create_registry_key(path) 68 | 69 | @handle_api_errors 70 | def openURL(self, url: str): 71 | from webbrowser import open 72 | 73 | open(url) 74 | 75 | @handle_api_errors 76 | def getIfeoData(self): 77 | from py.IFEO import get_ifeo_data 78 | 79 | return get_ifeo_data() 80 | 81 | @handle_api_errors 82 | def getSchedulingInfo(self): 83 | from py.SCHEDULING import get_scheduling_info 84 | 85 | return get_scheduling_info() 86 | 87 | @handle_api_errors 88 | def getPowerSettings(self): 89 | from py.POWER_SETTINGS import get_power_settings 90 | 91 | return get_power_settings() 92 | 93 | @handle_api_errors 94 | def writeValueIndex( 95 | self, 96 | scheme_guid_str: str, 97 | subgroup_guid_str: str, 98 | setting_guid_str: str, 99 | value_index: int, 100 | is_ac: bool, 101 | ): 102 | from py.POWER_SETTINGS import write_value_index 103 | 104 | write_value_index( 105 | scheme_guid_str, subgroup_guid_str, setting_guid_str, value_index, is_ac 106 | ) 107 | 108 | @handle_api_errors 109 | def setActiveScheme(self, scheme_guid_str: str): 110 | from py.POWER_SETTINGS import set_active_scheme 111 | 112 | set_active_scheme(scheme_guid_str) 113 | 114 | @handle_api_errors 115 | def getCompatibilityOptions(self): 116 | from py.COMPATIBILITY_OPTIONS import get_compatibility_options 117 | 118 | return get_compatibility_options() 119 | 120 | @handle_api_errors 121 | def saveFile(self, file_name: str, file_types: tuple[str], content: str): 122 | path = window.create_file_dialog( 123 | SAVE_DIALOG, 124 | directory="/", 125 | save_filename=file_name, 126 | file_types=file_types, 127 | ) 128 | 129 | if path is None: 130 | return 131 | 132 | with open(str(path), "w", encoding="utf-8") as file: 133 | file.write(content) 134 | 135 | @handle_api_errors 136 | def exportPowerScheme(self, guid: str, file_name: str, file_types: tuple[str]): 137 | path = window.create_file_dialog( 138 | SAVE_DIALOG, 139 | directory="/", 140 | save_filename=file_name, 141 | file_types=file_types, 142 | ) 143 | 144 | if path is None: 145 | return 146 | 147 | from subprocess import CREATE_NO_WINDOW, run 148 | 149 | run( 150 | ["powercfg", "/export", str(path), guid], 151 | check=True, 152 | creationflags=CREATE_NO_WINDOW, 153 | ) 154 | 155 | 156 | def on_shown(): 157 | window.maximize() 158 | 159 | 160 | if __name__ == "__main__": 161 | isDevEnv = not path.exists(path.join(path.dirname(__file__), "gui/index.html")) 162 | entry_point = "http://localhost:5173/" if isDevEnv else "gui/index.html" 163 | 164 | window = create_window( 165 | "Windows MultiTool", entry_point, js_api=Api(), text_select=True 166 | ) 167 | window.events.shown += on_shown 168 | 169 | start(ssl=True, debug=isDevEnv) 170 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell, Button, Center, NavLink } from "@mantine/core"; 2 | import { IconDownload } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import packageJson from "../package.json"; 5 | import COMPATIBILITY_OPTIONS, { 6 | type CompatibilityOptionsData, 7 | } from "./COMPATIBILITY_OPTIONS"; 8 | import DISPLAY_INFO, { type DisplayInfo } from "./DISPLAY_INFO"; 9 | import IFEO, { type IfeoData } from "./IFEO"; 10 | import PCI_IRQs, { type SystemInfo } from "./PCI_IRQs"; 11 | import POWER_SETTINGS, { type PowerSettings } from "./POWER_SETTINGS"; 12 | import SCHEDULING, { type SchedulingInfo } from "./SCHEDULING"; 13 | 14 | declare global { 15 | interface Window { 16 | pywebview: { 17 | api: { 18 | openURL: (url: string) => Promise; 19 | getSystemInfo: () => Promise; 20 | getDisplayInfo: () => Promise; 21 | getIfeoData: () => Promise; 22 | getSchedulingInfo: () => Promise; 23 | getPowerSettings: () => Promise; 24 | writeValueIndex: ( 25 | schemeGuidStr: string, 26 | subgroupGuidStr: string, 27 | settingGuidStr: string, 28 | valueIndex: number, 29 | isAc: boolean 30 | ) => Promise; 31 | setActiveScheme: (schemeGuidStr: string) => Promise; 32 | writeRegistryValue: ( 33 | hkeyStr: "HKLM" | "HKCU", 34 | path: string, 35 | name: string, 36 | type: number, 37 | value: number | string 38 | ) => Promise; 39 | deleteRegistryValue: ( 40 | hkeyStr: "HKLM" | "HKCU", 41 | path: string, 42 | value: string 43 | ) => Promise; 44 | deleteRegistryKey: (path: string, key: string) => Promise; 45 | createRegistryKey: (path: string) => Promise; 46 | getCompatibilityOptions: () => Promise; 47 | saveFile: ( 48 | fileName: string, 49 | fileTypes: string[], 50 | content: string 51 | ) => Promise; 52 | exportPowerScheme: ( 53 | guid: string, 54 | fileName: string, 55 | fileTypes: string[] 56 | ) => Promise; 57 | }; 58 | }; 59 | } 60 | } 61 | 62 | const navItems = [ 63 | { name: "PCI IRQs", component: }, 64 | { name: "Display Info", component: }, 65 | { name: "IFEO", component: }, 66 | { name: "Scheduling", component: }, 67 | { name: "Power Settings", component: }, 68 | { name: "Compatibility Options", component: }, 69 | ] as const; 70 | 71 | export default function App() { 72 | const [apiReady, setApiReady] = useState(false); 73 | const [active, setActive] = useState< 74 | (typeof navItems)[number]["name"] | undefined 75 | >(); 76 | const [updateAvailable, setUpdateAvailable] = useState(false); 77 | 78 | useEffect(() => { 79 | window.addEventListener("pywebviewready", () => { 80 | setApiReady(true); 81 | }); 82 | 83 | fetch(`https://api.github.com/repos/${packageJson.repo}/releases/latest`) 84 | .then((response) => { 85 | if (!response.ok) { 86 | throw new Error(response.statusText); 87 | } 88 | 89 | return response.json(); 90 | }) 91 | .then((data: { tag_name: string }) => { 92 | if (data.tag_name !== packageJson.version) { 93 | setUpdateAvailable(true); 94 | } 95 | }) 96 | .catch((error: unknown) => { 97 | console.error(error); 98 | }); 99 | }, []); 100 | 101 | if (!apiReady) { 102 | return; 103 | } 104 | 105 | return ( 106 | <> 107 | 108 | {navItems.map((item) => { 109 | const { name } = item; 110 | 111 | return ( 112 | { 117 | setActive(name); 118 | }} 119 | /> 120 | ); 121 | })} 122 | 123 | 124 | 125 | {active 126 | ? navItems.find((item) => item.name === active)?.component 127 | : updateAvailable && ( 128 |
129 | 150 |
151 | )} 152 |
153 | 154 | ); 155 | } 156 | -------------------------------------------------------------------------------- /src/POWER_SETTINGS/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Menu, Table, Tabs } from "@mantine/core"; 2 | import { IconChevronDown } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import s2 from "../App.module.css"; 5 | import s from "./index.module.css"; 6 | import PossibleValues from "./PossibleValues"; 7 | import Values from "./Values"; 8 | 9 | export interface PowerSettings { 10 | activeSchemeGuid: string; 11 | powerSchemes: PowerSchemes; 12 | } 13 | 14 | type PowerSchemes = { 15 | guid: string; 16 | name: string; 17 | settings: Setting[]; 18 | }[]; 19 | 20 | interface Subgroup { 21 | guid: string; 22 | name: string | null; 23 | } 24 | 25 | interface Option { 26 | index: number; 27 | name: string; 28 | description: string; 29 | } 30 | 31 | interface Range { 32 | min: number; 33 | max: number; 34 | increment: number; 35 | unit: string; 36 | } 37 | 38 | export interface Setting { 39 | guid: string; 40 | name: string; 41 | description: string; 42 | options: Option[] | null; 43 | range: Range | null; 44 | subgroup: Subgroup; 45 | ac: number; 46 | dc: number; 47 | } 48 | 49 | export default function POWER_SETTINGS() { 50 | const [powerSettings, setPowerSettings] = useState(); 51 | 52 | useEffect(() => { 53 | window.pywebview.api 54 | .getPowerSettings() 55 | .then((data) => { 56 | setPowerSettings(data); 57 | }) 58 | .catch((error: unknown) => { 59 | alert(error instanceof Error ? error.toString() : error); 60 | }); 61 | }, []); 62 | 63 | if (!powerSettings) { 64 | return; 65 | } 66 | 67 | const { powerSchemes, activeSchemeGuid } = powerSettings; 68 | 69 | const activeSchemeIndex = powerSchemes.findIndex( 70 | (scheme) => scheme.guid === activeSchemeGuid 71 | ); 72 | 73 | return ( 74 | <> 75 |
76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | EXPORT 85 | 86 | { 88 | const currentScheme = powerSchemes[activeSchemeIndex]; 89 | 90 | window.pywebview.api 91 | .exportPowerScheme( 92 | currentScheme.guid, 93 | `${currentScheme.name}.pow`, 94 | ["Power Plan (*.pow)"] 95 | ) 96 | .catch((error: unknown) => { 97 | alert(error instanceof Error ? error.toString() : error); 98 | }); 99 | }} 100 | > 101 | .pow 102 | 103 | 104 | 105 |
106 | 107 | 108 | 109 | {powerSchemes.map((powerScheme, index) => ( 110 | { 114 | window.pywebview.api 115 | .setActiveScheme(powerScheme.guid) 116 | .catch((error: unknown) => { 117 | alert(error instanceof Error ? error.toString() : error); 118 | }); 119 | }} 120 | > 121 | {powerScheme.name} 122 | 123 | ))} 124 | 125 | 126 | 127 | 128 | 129 | Subgroup 130 | Setting 131 | Value 132 | Possible Values 133 | Description 134 | 135 | 136 | 137 | 138 | {powerSchemes[activeSchemeIndex].settings.map( 139 | (setting, settingIndex) => ( 140 | 141 | {setting.subgroup.name} 142 | 143 | {setting.name} 144 | 145 | 146 | {powerSchemes.map((powerScheme, powerSchemeIndex) => ( 147 | 151 | 156 | 157 | ))} 158 | 159 | 160 | 161 | 162 | 163 | 164 | {setting.description} 165 | 166 | ) 167 | )} 168 | 169 |
170 |
171 | 172 | ); 173 | } 174 | -------------------------------------------------------------------------------- /src/COMPATIBILITY_OPTIONS/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Menu, Table } from "@mantine/core"; 2 | import { IconChevronDown } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import s from "../App.module.css"; 5 | import TableComponent from "./Table"; 6 | 7 | export type CompatibilityOptions = [ 8 | null | "~", 9 | null | "DISABLEDXMAXIMIZEDWINDOWEDMODE", 10 | null | "RUNASADMIN", 11 | null | "640X480", 12 | null | "PERPROCESSSYSTEMDPIFORCEOFF" | "PERPROCESSSYSTEMDPIFORCEON", 13 | null | "HIGHDPIAWARE" | "DPIUNAWARE" | "GDIDPISCALING DPIUNAWARE", 14 | null | "256COLOR", 15 | null | "16BITCOLOR", 16 | null | "TRANSFORMLEGACYCOLORMANAGED", 17 | ( 18 | | null 19 | | "WIN95" 20 | | "WIN98" 21 | | "WINXPSP2" 22 | | "WINXPSP3" 23 | | "VISTARTM" 24 | | "VISTASP1" 25 | | "VISTASP2" 26 | | "WIN7RTM" 27 | | "WIN8RTM" 28 | ) 29 | ]; 30 | 31 | export interface CompatibilityOptionsData { 32 | HKLM: HiveData; 33 | HKCU: HiveData; 34 | } 35 | 36 | type HiveData = Map; 37 | 38 | export const COMPAT_PATH = 39 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"; 40 | 41 | function processHive( 42 | hive: HiveData, 43 | hiveKey: "HKEY_LOCAL_MACHINE" | "HKEY_CURRENT_USER", 44 | isReg: boolean 45 | ) { 46 | if (hive.size === 0) { 47 | return ""; 48 | } 49 | 50 | const regPath = `${hiveKey}\\${COMPAT_PATH}`; 51 | 52 | let content = ""; 53 | 54 | if (isReg) { 55 | content = `[${regPath}]\n`; 56 | } else { 57 | content = `set "REG_PATH=${regPath}"\n\n`; 58 | } 59 | 60 | for (const [name, options] of hive) { 61 | const value = options.filter((x) => x !== null).join(" "); 62 | 63 | if (value) { 64 | if (isReg) { 65 | const escapedName = name.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); 66 | content += `"${escapedName}"="${value}"\n`; 67 | } else { 68 | content += `reg add "%REG_PATH%" /v "${name}" /t REG_SZ /d "${value}" /f\n`; 69 | } 70 | } 71 | } 72 | 73 | return content + "\n"; 74 | } 75 | 76 | export default function COMPATIBILITY_OPTIONS() { 77 | const [compatibilityOptionsData, setCompatibilityOptionsData] = 78 | useState(); 79 | 80 | useEffect(() => { 81 | window.pywebview.api 82 | .getCompatibilityOptions() 83 | .then((data) => { 84 | setCompatibilityOptionsData({ 85 | HKLM: new Map(Object.entries(data.HKLM)) as HiveData, 86 | HKCU: new Map(Object.entries(data.HKCU)) as HiveData, 87 | }); 88 | }) 89 | .catch((error: unknown) => { 90 | alert(error instanceof Error ? error.toString() : error); 91 | }); 92 | }, []); 93 | 94 | if (!compatibilityOptionsData) { 95 | return; 96 | } 97 | 98 | return ( 99 | <> 100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | EXPORT 110 | { 112 | const { HKLM, HKCU } = compatibilityOptionsData; 113 | 114 | if (HKLM.size !== 0 || HKCU.size !== 0) { 115 | window.pywebview.api 116 | .saveFile( 117 | "compatibility_settings.reg", 118 | ["Registry File (*.reg)"], 119 | "Windows Registry Editor Version 5.00\n\n" + 120 | processHive(HKLM, "HKEY_LOCAL_MACHINE", true) + 121 | processHive(HKCU, "HKEY_CURRENT_USER", true) 122 | ) 123 | .catch((error: unknown) => { 124 | alert(error instanceof Error ? error.toString() : error); 125 | }); 126 | } 127 | }} 128 | > 129 | .reg 130 | 131 | 132 | { 134 | const { HKLM, HKCU } = compatibilityOptionsData; 135 | 136 | if (HKLM.size !== 0 || HKCU.size !== 0) { 137 | window.pywebview.api 138 | .saveFile( 139 | "compatibility_settings.bat", 140 | ["Batch File (*.bat)"], 141 | "setlocal\n\n" + 142 | processHive(HKLM, "HKEY_LOCAL_MACHINE", false) + 143 | processHive(HKCU, "HKEY_CURRENT_USER", false) + 144 | "endlocal" 145 | ) 146 | .catch((error: unknown) => { 147 | alert(error instanceof Error ? error.toString() : error); 148 | }); 149 | } 150 | }} 151 | > 152 | .bat 153 | 154 | 155 | 156 |
157 | 158 | 159 | 160 | 161 | Process Path 162 | ~ 163 | Disable FSO 164 | Run as admin 165 | Run in 640x480 166 | Override system DPI 167 | Override high DPI scaling behavior 168 | Reduce color mode (8-bit 256) 169 | Reduce color mode (16-bit 65536) 170 | Use legacy display ICC color management 171 | Windows version 172 | 173 | 174 | 175 | 180 | 185 |
186 | 187 | ); 188 | } 189 | -------------------------------------------------------------------------------- /src/IFEO/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Group, Menu, Table } from "@mantine/core"; 2 | import { IconChevronDown, IconMinus } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import s from "../App.module.css"; 5 | import { formatRegValue } from "../shared"; 6 | import AddNewProcess from "./AddProcess"; 7 | import CpuPriorityField from "./CpuPriority"; 8 | import IoPriorityField from "./IoPriority"; 9 | import PagePriorityField from "./PagePriority"; 10 | 11 | export interface IfeoDataValue { 12 | Path: string; 13 | CpuPriorityClass: number | null; 14 | IoPriority: number | null; 15 | PagePriority: number | null; 16 | } 17 | 18 | export type IfeoData = Map; 19 | 20 | export const IFEO_PATH = 21 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"; 22 | 23 | function getExportData(ifeoData: IfeoData, isReg: boolean) { 24 | let content = ""; 25 | 26 | if (isReg) { 27 | content += "Windows Registry Editor Version 5.00\n\n"; 28 | 29 | for (const [name, data] of ifeoData) { 30 | content += 31 | `[HKEY_LOCAL_MACHINE\\${IFEO_PATH}\\${name}\\PerfOptions]\n` + 32 | formatRegValue("CpuPriorityClass", data.CpuPriorityClass) + 33 | formatRegValue("IoPriority", data.IoPriority) + 34 | formatRegValue("PagePriority", data.PagePriority) + 35 | "\n"; 36 | } 37 | } else { 38 | content += 39 | "setlocal\n\n" + `set "IFEO_BASE=HKEY_LOCAL_MACHINE\\${IFEO_PATH}"\n\n`; 40 | 41 | for (const [name, data] of ifeoData) { 42 | content += `set "APP_KEY=%IFEO_BASE%\\${name}\\PerfOptions"\n`; 43 | 44 | const baseAdd = `reg add "%APP_KEY%"`; 45 | const baseDelete = `reg delete "%APP_KEY%"`; 46 | 47 | if (data.CpuPriorityClass !== null) { 48 | content += `${baseAdd} /v CpuPriorityClass /t REG_DWORD /d ${data.CpuPriorityClass.toString()} /f\n`; 49 | } else { 50 | content += `${baseDelete} /v CpuPriorityClass /f\n`; 51 | } 52 | 53 | if (data.IoPriority !== null) { 54 | content += `${baseAdd} /v IoPriority /t REG_DWORD /d ${data.IoPriority.toString()} /f\n`; 55 | } else { 56 | content += `${baseDelete} /v IoPriority /f\n`; 57 | } 58 | 59 | if (data.PagePriority !== null) { 60 | content += `${baseAdd} /v PagePriority /t REG_DWORD /d ${data.PagePriority.toString()} /f\n`; 61 | } else { 62 | content += `${baseDelete} /v PagePriority /f\n`; 63 | } 64 | 65 | content += "\n"; 66 | } 67 | 68 | content += "endlocal"; 69 | } 70 | 71 | return content; 72 | } 73 | 74 | export default function IFEO() { 75 | const [ifeoData, setIfeoData] = useState(); 76 | 77 | useEffect(() => { 78 | window.pywebview.api 79 | .getIfeoData() 80 | .then((data) => { 81 | setIfeoData(new Map(Object.entries(data))); 82 | }) 83 | .catch((error: unknown) => { 84 | alert(error instanceof Error ? error.toString() : error); 85 | }); 86 | }, []); 87 | 88 | if (!ifeoData) { 89 | return; 90 | } 91 | 92 | return ( 93 | <> 94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | EXPORT 104 | { 106 | if (ifeoData.size !== 0) { 107 | window.pywebview.api 108 | .saveFile( 109 | "ifeo.reg", 110 | ["Registry File (*.reg)"], 111 | getExportData(ifeoData, true) 112 | ) 113 | .catch((error: unknown) => { 114 | alert(error instanceof Error ? error.toString() : error); 115 | }); 116 | } 117 | }} 118 | > 119 | .reg 120 | 121 | 122 | { 124 | if (ifeoData.size !== 0) { 125 | window.pywebview.api 126 | .saveFile( 127 | "ifeo.bat", 128 | ["Batch File (*.bat)"], 129 | getExportData(ifeoData, false) 130 | ) 131 | .catch((error: unknown) => { 132 | alert(error instanceof Error ? error.toString() : error); 133 | }); 134 | } 135 | }} 136 | > 137 | .bat 138 | 139 | 140 | 141 |
142 | 143 | 144 | 145 | 146 | Process 147 | CPU Priority Class 148 | IO Priority 149 | Memory Priority 150 | 151 | 152 | 153 | 154 | {[...ifeoData.entries()] 155 | .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) 156 | .map(([process_name, data]) => ( 157 | 158 | 159 | 160 | { 165 | window.pywebview.api 166 | .deleteRegistryKey(data.Path, "PerfOptions") 167 | .then(() => { 168 | setIfeoData((prev) => { 169 | if (!prev) { 170 | return prev; 171 | } 172 | 173 | const newData = new Map(prev); 174 | newData.delete(process_name); 175 | 176 | return newData; 177 | }); 178 | }) 179 | .catch((error: unknown) => { 180 | alert( 181 | error instanceof Error ? error.toString() : error 182 | ); 183 | }); 184 | }} 185 | > 186 | 187 | 188 | {process_name} 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | ))} 205 | 206 | 207 | 208 | 209 | 210 | 211 |
212 | 213 | ); 214 | } 215 | -------------------------------------------------------------------------------- /src/DISPLAY_INFO/index.tsx: -------------------------------------------------------------------------------- 1 | import { Group, Table } from "@mantine/core"; 2 | import { useEffect, useState } from "react"; 3 | import s from "./index.module.css"; 4 | 5 | const DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY = { 6 | [-1]: "Other", 7 | [0]: "HD-15 (VGA)", 8 | [1]: "S-Video", 9 | [2]: "Composite Video", 10 | [3]: "Component Video", 11 | [4]: "DVI", 12 | [5]: "HDMI", 13 | [6]: "LVDS", 14 | [8]: "D-Terminal", 15 | [9]: "SDI", 16 | [10]: "DisplayPort External", 17 | [11]: "DisplayPort Embedded", 18 | [12]: "UDI External", 19 | [13]: "UDI Embedded", 20 | [14]: "SDTV Dongle", 21 | [15]: "Miracast", 22 | [16]: "Indirect Wired", 23 | [17]: "Indirect Virtual", 24 | [18]: "DisplayPort USB Tunnel", 25 | [0x80000000]: "Internal", 26 | [0xffffffff]: "Force UInt32", 27 | } as const; 28 | 29 | const DISPLAYCONFIG_ROTATION = { 30 | [1]: "0 °", 31 | [2]: "90 °", 32 | [3]: "180 °", 33 | [4]: "270 °", 34 | [0xffffffff]: "Force UInt32", 35 | } as const; 36 | 37 | const DISPLAYCONFIG_SCALING = { 38 | [1]: "Identity", 39 | [2]: "Centered", 40 | [3]: "Stretched", 41 | [4]: "AspectRatioCenteredMax", 42 | [5]: "Custom", 43 | [128]: "Preferred", 44 | [0xffffffff]: "Force UInt32", 45 | } as const; 46 | 47 | const DISPLAYCONFIG_PIXELFORMAT = { 48 | [1]: "8 BPP", 49 | [2]: "16 BPP", 50 | [3]: "24 BPP", 51 | [4]: "32 BPP", 52 | [5]: "NONGDI", 53 | [0xffffffff]: "Force UInt32", 54 | } as const; 55 | 56 | export interface DisplayInfo { 57 | displays: { 58 | monitorFriendlyDeviceName: string; 59 | monitorDevicePath: string; 60 | adapterDevicePath: string; 61 | outputTechnology: number; 62 | rotation: number; 63 | refreshRate: number; 64 | horizontalFrequency: number; 65 | resolution: string; 66 | pixelRate: number; 67 | scaling: number; 68 | pixelFormat: number; 69 | mpo: { 70 | MaxPlanes: number; 71 | MaxRGBPlanes: number; 72 | MaxYUVPlanes: number; 73 | MaxStretchFactor: number; 74 | MaxShrinkFactor: number; 75 | caps: { 76 | Rotation: number; 77 | RotationWithoutIndependentFlip: number; 78 | VerticalFlip: number; 79 | HorizontalFlip: number; 80 | StretchRGB: number; 81 | StretchYUV: number; 82 | BilinearFilter: number; 83 | HighFilter: number; 84 | Shared: number; 85 | Immediate: number; 86 | Plane0ForVirtualModeOnly: number; 87 | Version3DDISupport: number; 88 | }; 89 | }; 90 | }[]; 91 | } 92 | 93 | export default function DISPLAY_INFO() { 94 | const [displayInfo, setDisplayInfo] = useState(); 95 | 96 | useEffect(() => { 97 | window.pywebview.api 98 | .getDisplayInfo() 99 | .then((data) => { 100 | setDisplayInfo(data); 101 | }) 102 | .catch((error: unknown) => { 103 | alert(error instanceof Error ? error.toString() : error); 104 | }); 105 | }, []); 106 | 107 | if (!displayInfo) { 108 | return; 109 | } 110 | 111 | return ( 112 | 113 | 114 | 115 | 116 | {displayInfo.displays.map((display) => ( 117 | 118 | {display.monitorFriendlyDeviceName} 119 |
{display.monitorDevicePath}
120 |
121 | ))} 122 |
123 |
124 | 125 | 126 | 127 | Output Technology 128 | {displayInfo.displays.map((display) => ( 129 | 130 | { 131 | DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY[ 132 | display.outputTechnology as keyof typeof DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY 133 | ] 134 | } 135 | 136 | ))} 137 | 138 | 139 | 140 | Rotation 141 | {displayInfo.displays.map((display) => ( 142 | 143 | { 144 | DISPLAYCONFIG_ROTATION[ 145 | display.rotation as keyof typeof DISPLAYCONFIG_ROTATION 146 | ] 147 | } 148 | 149 | ))} 150 | 151 | 152 | 153 | Refresh Rate 154 | {displayInfo.displays.map((display) => ( 155 | 156 | {display.refreshRate.toString() + " Hz"} 157 | 158 | ))} 159 | 160 | 161 | 162 | Horizontal Frequency 163 | {displayInfo.displays.map((display) => ( 164 | 165 | {display.horizontalFrequency.toString() + " kHz"} 166 | 167 | ))} 168 | 169 | 170 | 171 | Resolution 172 | {displayInfo.displays.map((display) => ( 173 | 174 | {display.resolution} 175 | 176 | ))} 177 | 178 | 179 | 180 | Pixel Rate 181 | {displayInfo.displays.map((display) => ( 182 | 183 | {display.pixelRate.toString() + " MHz"} 184 | 185 | ))} 186 | 187 | 188 | 189 | Scaling 190 | {displayInfo.displays.map((display) => ( 191 | 192 | { 193 | DISPLAYCONFIG_SCALING[ 194 | display.scaling as keyof typeof DISPLAYCONFIG_SCALING 195 | ] 196 | } 197 | 198 | ))} 199 | 200 | 201 | 202 | Pixel Format 203 | {displayInfo.displays.map((display) => ( 204 | 205 | { 206 | DISPLAYCONFIG_PIXELFORMAT[ 207 | display.pixelFormat as keyof typeof DISPLAYCONFIG_PIXELFORMAT 208 | ] 209 | } 210 | 211 | ))} 212 | 213 | 214 | 215 | MPO Capabilities 216 | {displayInfo.displays.map((display) => { 217 | const { mpo } = display; 218 | 219 | return ( 220 | 221 | {Object.entries(mpo).map(([key, value]) => { 222 | if (key !== "caps") { 223 | return ( 224 | 225 |
{key}
226 |
{value as number}
227 |
228 | ); 229 | } 230 | })} 231 | 232 |
233 | 234 | {Object.entries(mpo.caps).map(([key, value]) => ( 235 | 236 |
{key}
237 |
238 | {value ? "✓" : "✗"} 239 |
240 |
241 | ))} 242 |
243 | ); 244 | })} 245 |
246 |
247 |
248 | ); 249 | } 250 | -------------------------------------------------------------------------------- /src/PCI_IRQs/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon, Menu, Table } from "@mantine/core"; 2 | import { IconChevronDown } from "@tabler/icons-react"; 3 | import { useEffect, useState } from "react"; 4 | import s2 from "../App.module.css"; 5 | import { formatBitMask, formatRegValue } from "../shared"; 6 | import AssignmentSetOverrideField from "./AssignmentSetOverride"; 7 | import DevicePolicyField from "./DevicePolicy"; 8 | import DevicePriorityField from "./DevicePriority"; 9 | import s from "./index.module.css"; 10 | import MessageNumberLimitField from "./MessageNumberLimit"; 11 | import MSISupportedField from "./MSISupported"; 12 | 13 | export interface SystemInfo { 14 | cpu: { cpus: number }; 15 | devices: { 16 | DeviceId: string; 17 | Path: string; 18 | DeviceName: string; 19 | DevicePriority: number | null; 20 | DevicePolicy: number | null; 21 | AssignmentSetOverride: number | null; 22 | MessageNumberLimit: number | null; 23 | MSISupported: number | null; 24 | InterruptSupport: number | null; 25 | MaximumMessageNumberLimit: number | null; 26 | }[]; 27 | } 28 | 29 | export const AFFINITY_PATH = 30 | "\\Device Parameters\\Interrupt Management\\Affinity Policy"; 31 | export const MSI_PATH = 32 | "\\Device Parameters\\Interrupt Management\\MessageSignaledInterruptProperties"; 33 | 34 | function getExportData(systemInfo: SystemInfo, isReg: boolean) { 35 | let content = ""; 36 | 37 | if (isReg) { 38 | content += "Windows Registry Editor Version 5.00\n\n"; 39 | 40 | for (const device of systemInfo.devices) { 41 | content += 42 | `[HKEY_LOCAL_MACHINE\\${device.Path}${AFFINITY_PATH}]\n` + 43 | formatRegValue("DevicePriority", device.DevicePriority) + 44 | formatRegValue("DevicePolicy", device.DevicePolicy) + 45 | `"AssignmentSetOverride"=${formatBitMask( 46 | device.AssignmentSetOverride, 47 | true 48 | )}\n\n` + 49 | `[HKEY_LOCAL_MACHINE\\${device.Path}${MSI_PATH}]\n` + 50 | formatRegValue("MSISupported", device.MSISupported) + 51 | formatRegValue("MessageNumberLimit", device.MessageNumberLimit) + 52 | "\n"; 53 | } 54 | } else { 55 | content += 56 | "setlocal\n\n" + 57 | `set "AFFINITY_PATH=${AFFINITY_PATH}"\n` + 58 | `set "MSI_PATH=${MSI_PATH}"\n\n`; 59 | 60 | for (const device of systemInfo.devices) { 61 | content += `set "BASE=${device.Path}"\n\n`; 62 | 63 | const baseAddAffinity = `reg add "%BASE%%AFFINITY_PATH%"`; 64 | const baseDeleteAffinity = `reg delete "%BASE%%AFFINITY_PATH%"`; 65 | const baseAddMsi = `reg add "%BASE%%MSI_PATH%"`; 66 | const baseDeleteMsi = `reg delete "%BASE%%MSI_PATH%"`; 67 | 68 | if (device.DevicePriority !== null) { 69 | content += `${baseAddAffinity} /v DevicePriority /t REG_DWORD /d ${device.DevicePriority.toString()} /f \n`; 70 | } else { 71 | content += `${baseDeleteAffinity} /v DevicePriority /f \n`; 72 | } 73 | 74 | if (device.DevicePolicy !== null) { 75 | content += `${baseAddAffinity} /v DevicePolicy /t REG_DWORD /d ${device.DevicePolicy.toString()} /f \n`; 76 | } else { 77 | content += `${baseDeleteAffinity} /v DevicePolicy /f \n`; 78 | } 79 | 80 | if (device.AssignmentSetOverride !== null) { 81 | content += `${baseAddAffinity} /v AssignmentSetOverride /t REG_BINARY /d ${formatBitMask( 82 | device.AssignmentSetOverride, 83 | false 84 | )} /f \n`; 85 | } else { 86 | content += `${baseDeleteAffinity} /v AssignmentSetOverride /f \n`; 87 | } 88 | 89 | if (device.MSISupported !== null) { 90 | content += `${baseAddMsi} /v MSISupported /t REG_DWORD /d ${device.MSISupported.toString()} /f \n`; 91 | } else { 92 | content += `${baseDeleteMsi} /v MSISupported /f \n`; 93 | } 94 | 95 | if (device.MessageNumberLimit !== null) { 96 | content += `${baseAddMsi} /v MessageNumberLimit /t REG_DWORD /d ${device.MessageNumberLimit.toString()} /f \n`; 97 | } else { 98 | content += `${baseDeleteMsi} /v MessageNumberLimit /f \n`; 99 | } 100 | 101 | content += "\n"; 102 | } 103 | 104 | content += "endlocal"; 105 | } 106 | 107 | return content; 108 | } 109 | 110 | function formatInterruptSupport(value: number | null) { 111 | if (value === null) { 112 | return; 113 | } 114 | 115 | const supportedTypes: string[] = []; 116 | if (value & 0x1) { 117 | supportedTypes.push("Line"); 118 | } 119 | if (value & 0x2) { 120 | supportedTypes.push("MSI"); 121 | } 122 | if (value & 0x4) { 123 | supportedTypes.push("MSI-X"); 124 | } 125 | 126 | return supportedTypes.length > 0 ? supportedTypes.join(", ") : "-"; 127 | } 128 | 129 | export default function PCI_IRQs() { 130 | const [systemInfo, setSystemInfo] = useState(); 131 | 132 | useEffect(() => { 133 | window.pywebview.api 134 | .getSystemInfo() 135 | .then((data) => { 136 | setSystemInfo(data); 137 | }) 138 | .catch((error: unknown) => { 139 | alert(error instanceof Error ? error.toString() : error); 140 | }); 141 | }, []); 142 | 143 | if (!systemInfo) { 144 | return; 145 | } 146 | 147 | return ( 148 | <> 149 |
150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | EXPORT 159 | { 161 | window.pywebview.api 162 | .saveFile( 163 | "pci_irqs.reg", 164 | ["Registry File (*.reg)"], 165 | getExportData(systemInfo, true) 166 | ) 167 | .catch((error: unknown) => { 168 | alert(error instanceof Error ? error.toString() : error); 169 | }); 170 | }} 171 | > 172 | .reg 173 | 174 | 175 | { 177 | window.pywebview.api 178 | .saveFile( 179 | "pci_irqs.bat", 180 | ["Batch File (*.bat)"], 181 | getExportData(systemInfo, false) 182 | ) 183 | .catch((error: unknown) => { 184 | alert(error instanceof Error ? error.toString() : error); 185 | }); 186 | }} 187 | > 188 | .bat 189 | 190 | 191 | 192 |
193 | 194 | 195 | 196 | 197 | Device 198 | IRQ Priority 199 | IRQ Policy 200 | CPUs 201 | MSI 202 | Message Limit 203 | Max Limit 204 | Interrupt Type 205 | 206 | 207 | 208 | 209 | {systemInfo.devices.map((device) => ( 210 | 211 | 212 | {device.DeviceName} 213 |
{device.DeviceId}
214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | {device.MaximumMessageNumberLimit} 240 | 241 | 242 | {formatInterruptSupport(device.InterruptSupport)} 243 | 244 |
245 | ))} 246 |
247 |
248 | 249 | ); 250 | } 251 | -------------------------------------------------------------------------------- /src/COMPATIBILITY_OPTIONS/Options.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox, Select, Table } from "@mantine/core"; 2 | import { useState } from "react"; 3 | import { COMPAT_PATH, type CompatibilityOptions } from "."; 4 | import { REGISTRY_DATA_TYPES } from "../shared"; 5 | 6 | export default function Options({ 7 | name, 8 | compatibilityOptions, 9 | hiveStr, 10 | }: { 11 | name: string; 12 | compatibilityOptions: CompatibilityOptions; 13 | hiveStr: "HKLM" | "HKCU"; 14 | }) { 15 | const [data, setData] = useState([ 16 | ...compatibilityOptions, 17 | ]); 18 | 19 | return ( 20 | <> 21 | 22 | { 25 | const newData: CompatibilityOptions = [...data]; 26 | newData[0] = ev.currentTarget.checked ? "~" : null; 27 | 28 | window.pywebview.api 29 | .writeRegistryValue( 30 | hiveStr, 31 | COMPAT_PATH, 32 | name, 33 | REGISTRY_DATA_TYPES.REG_SZ, 34 | newData.filter((x) => x !== null).join(" ") 35 | ) 36 | .then(() => { 37 | setData(newData); 38 | }) 39 | .catch((error: unknown) => { 40 | alert(error instanceof Error ? error.toString() : error); 41 | }); 42 | }} 43 | /> 44 | 45 | 46 | 47 | { 50 | const newData: CompatibilityOptions = [...data]; 51 | newData[1] = ev.currentTarget.checked 52 | ? "DISABLEDXMAXIMIZEDWINDOWEDMODE" 53 | : null; 54 | 55 | window.pywebview.api 56 | .writeRegistryValue( 57 | hiveStr, 58 | COMPAT_PATH, 59 | name, 60 | REGISTRY_DATA_TYPES.REG_SZ, 61 | newData.filter((x) => x !== null).join(" ") 62 | ) 63 | .then(() => { 64 | setData(newData); 65 | }) 66 | .catch((error: unknown) => { 67 | alert(error instanceof Error ? error.toString() : error); 68 | }); 69 | }} 70 | /> 71 | 72 | 73 | 74 | { 77 | const newData: CompatibilityOptions = [...data]; 78 | newData[2] = ev.currentTarget.checked ? "RUNASADMIN" : null; 79 | 80 | window.pywebview.api 81 | .writeRegistryValue( 82 | hiveStr, 83 | COMPAT_PATH, 84 | name, 85 | REGISTRY_DATA_TYPES.REG_SZ, 86 | newData.filter((x) => x !== null).join(" ") 87 | ) 88 | .then(() => { 89 | setData(newData); 90 | }) 91 | .catch((error: unknown) => { 92 | alert(error instanceof Error ? error.toString() : error); 93 | }); 94 | }} 95 | /> 96 | 97 | 98 | 99 | { 102 | const newData: CompatibilityOptions = [...data]; 103 | newData[3] = ev.currentTarget.checked ? "640X480" : null; 104 | 105 | window.pywebview.api 106 | .writeRegistryValue( 107 | hiveStr, 108 | COMPAT_PATH, 109 | name, 110 | REGISTRY_DATA_TYPES.REG_SZ, 111 | newData.filter((x) => x !== null).join(" ") 112 | ) 113 | .then(() => { 114 | setData(newData); 115 | }) 116 | .catch((error: unknown) => { 117 | alert(error instanceof Error ? error.toString() : error); 118 | }); 119 | }} 120 | /> 121 | 122 | 123 | 124 | { 158 | const newData: CompatibilityOptions = [...data]; 159 | newData[5] = value as (typeof newData)[5]; 160 | 161 | window.pywebview.api 162 | .writeRegistryValue( 163 | hiveStr, 164 | COMPAT_PATH, 165 | name, 166 | REGISTRY_DATA_TYPES.REG_SZ, 167 | newData.filter((x) => x !== null).join(" ") 168 | ) 169 | .then(() => { 170 | setData(newData); 171 | }) 172 | .catch((error: unknown) => { 173 | alert(error instanceof Error ? error.toString() : error); 174 | }); 175 | }} 176 | /> 177 | 178 | 179 | 180 | { 183 | const newData: CompatibilityOptions = [...data]; 184 | newData[6] = ev.currentTarget.checked ? "256COLOR" : null; 185 | 186 | window.pywebview.api 187 | .writeRegistryValue( 188 | hiveStr, 189 | COMPAT_PATH, 190 | name, 191 | REGISTRY_DATA_TYPES.REG_SZ, 192 | newData.filter((x) => x !== null).join(" ") 193 | ) 194 | .then(() => { 195 | setData(newData); 196 | }) 197 | .catch((error: unknown) => { 198 | alert(error instanceof Error ? error.toString() : error); 199 | }); 200 | }} 201 | /> 202 | 203 | 204 | 205 | { 208 | const newData: CompatibilityOptions = [...data]; 209 | newData[7] = ev.currentTarget.checked ? "16BITCOLOR" : null; 210 | 211 | window.pywebview.api 212 | .writeRegistryValue( 213 | hiveStr, 214 | COMPAT_PATH, 215 | name, 216 | REGISTRY_DATA_TYPES.REG_SZ, 217 | newData.filter((x) => x !== null).join(" ") 218 | ) 219 | .then(() => { 220 | setData(newData); 221 | }) 222 | .catch((error: unknown) => { 223 | alert(error instanceof Error ? error.toString() : error); 224 | }); 225 | }} 226 | /> 227 | 228 | 229 | 230 | { 233 | const newData: CompatibilityOptions = [...data]; 234 | newData[8] = ev.currentTarget.checked 235 | ? "TRANSFORMLEGACYCOLORMANAGED" 236 | : null; 237 | 238 | window.pywebview.api 239 | .writeRegistryValue( 240 | hiveStr, 241 | COMPAT_PATH, 242 | name, 243 | REGISTRY_DATA_TYPES.REG_SZ, 244 | newData.filter((x) => x !== null).join(" ") 245 | ) 246 | .then(() => { 247 | setData(newData); 248 | }) 249 | .catch((error: unknown) => { 250 | alert(error instanceof Error ? error.toString() : error); 251 | }); 252 | }} 253 | /> 254 | 255 | 256 | 257 |