From 5812065b56558fa52c955d9459522d8aaca249f7 Mon Sep 17 00:00:00 2001 From: OctoWoW Date: Thu, 9 Jul 2026 21:58:15 -0700 Subject: [PATCH] Fix monitor resolution issue with correct monitor detection Co-Authored-By: Claude Fable 5 --- package.json | 2 +- src/main/modules/displays.ts | 76 ++++++++++++++++++++++++++++++++++++ src/main/modules/mods.ts | 7 +++- 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/main/modules/displays.ts diff --git a/package.json b/package.json index 7e393f3..d264763 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "octo-launcher", - "version": "1.2.0", + "version": "1.2.1", "description": "An Electron application for launching and updating the OctoWoW client", "author": "OctoWoW", "copyright": "Copyright © 2026 OctoWoW", diff --git a/src/main/modules/displays.ts b/src/main/modules/displays.ts new file mode 100644 index 0000000..a1ee0eb --- /dev/null +++ b/src/main/modules/displays.ts @@ -0,0 +1,76 @@ +import os from 'node:os'; +import { spawn } from 'node:child_process'; + +import Logger from 'electron-log/main'; + +const SCRIPT = [ + '$ErrorActionPreference = "Stop"', + "Add-Type -TypeDefinition @'", + 'using System;', + 'using System.Runtime.InteropServices;', + 'public static class VmmfDisplays {', + ' [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]', + ' public struct DISPLAY_DEVICE {', + ' public int cb;', + ' [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string DeviceName;', + ' [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] public string DeviceString;', + ' public int StateFlags;', + ' [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] public string DeviceID;', + ' [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] public string DeviceKey;', + ' }', + ' [DllImport("user32.dll", EntryPoint="EnumDisplayDevicesA", CharSet=CharSet.Ansi)]', + ' public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);', + '}', + "'@", + '$dd = New-Object VmmfDisplays+DISPLAY_DEVICE', + '$dd.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($dd)', + 'for ($i = 0; [VmmfDisplays]::EnumDisplayDevices([NullString]::Value, $i, [ref]$dd, 0); $i++) {', + ' if ($dd.StateFlags -band 4) { Write-Output $i; exit 0 }', + '}', + 'exit 1' +].join('\n'); + +export const detectPrimaryDisplayIndex = (): Promise => { + if (os.platform() !== 'win32') return Promise.resolve(0); + + const encoded = Buffer.from(SCRIPT, 'utf16le').toString('base64'); + + return new Promise(resolve => { + let settled = false; + const finish = (index: number) => { + if (settled) return; + settled = true; + clearTimeout(timer); + resolve(index); + }; + + const child = spawn( + 'powershell.exe', + ['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded], + { windowsHide: true } + ); + + const timer = setTimeout(() => { + child.kill(); + Logger.warn('Primary display detection timed out'); + finish(0); + }, 8000); + + let stdout = ''; + child.stdout.on('data', d => (stdout += String(d))); + child.on('error', e => { + Logger.warn('Primary display detection failed to launch PowerShell', e); + finish(0); + }); + child.on('exit', code => { + const index = Number(stdout.trim()); + if (code === 0 && Number.isInteger(index) && index >= 0) { + Logger.info(`Detected primary display at device index ${index}`); + finish(index); + } else { + Logger.warn('Primary display detection failed, defaulting to 0'); + finish(0); + } + }); + }); +}; diff --git a/src/main/modules/mods.ts b/src/main/modules/mods.ts index cdaf799..d8eaf59 100644 --- a/src/main/modules/mods.ts +++ b/src/main/modules/mods.ts @@ -13,6 +13,7 @@ import Preferences from './preferences'; import Observable from './observable'; import Updater from './updater'; import { addDll, removeDll } from './dllsTxt'; +import { detectPrimaryDisplayIndex } from './displays'; const MOD_DOWNLOAD_TIMEOUT_MS = 60_000; @@ -113,8 +114,10 @@ class ModsClass extends Observable { if (clientDir) { const vmmfDll = path.join(clientDir, 'VanillaMultiMonitorFix.dll'); const vmmfCfg = path.join(clientDir, 'VMMFix_preferred_monitor.txt'); - if ((await fs.pathExists(vmmfDll)) && !(await fs.pathExists(vmmfCfg))) - await fs.writeFile(vmmfCfg, '0\n', 'utf8').catch(() => {}); + if ((await fs.pathExists(vmmfDll)) && !(await fs.pathExists(vmmfCfg))) { + const index = await detectPrimaryDisplayIndex(); + await fs.writeFile(vmmfCfg, `${index}\n`, 'utf8').catch(() => {}); + } } for (const m of MODS) {