Skip to content

Commit 08e4907

Browse files
authored
fix: fixed crash in Node 22+ by using Buffer instead of {} with EnumDisplaySettingsA (#17)
1 parent 4701d65 commit 08e4907

File tree

2 files changed

+29
-16
lines changed

2 files changed

+29
-16
lines changed

lib/commands/system.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import { NovaWindowsDriver } from '../driver';
33
import { getDisplayOrientation } from '../winapi/user32';
44

55
export function getOrientation(this: NovaWindowsDriver): Orientation {
6-
return getDisplayOrientation() % 2 ? 'PORTRAIT' : 'LANDSCAPE';
6+
return getDisplayOrientation();
77
}

lib/winapi/user32.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Orientation } from '@appium/types';
12
import {
23
load,
34
struct,
@@ -70,7 +71,7 @@ interface DeviceModeAnsi {
7071
dmDeviceName: string | null,
7172
dmSpecVersion: number,
7273
dmDriverVersion: number,
73-
dmSize: 68,
74+
dmSize: number,
7475
dmDriverExtra: number,
7576
dmFields: number,
7677
u1: {
@@ -213,7 +214,7 @@ const INPUT = struct('INPUT', {
213214
} satisfies InputUnion)
214215
});
215216

216-
struct('DEVMODEA', {
217+
const DEVMODEA = struct('DEVMODEA', {
217218
dmDeviceName: array('char', 32, 'String'),
218219
dmSpecVersion: 'uint16',
219220
dmDriverVersion: 'uint16',
@@ -291,7 +292,7 @@ const GetSystemMetrics = user32.func(/* c */ `int __stdcall GetSystemMetrics(int
291292
const SetProcessDPIAware = user32.func(/* c */ `bool __stdcall SetProcessDPIAware()`) as () => boolean;
292293
const GetDpiForSystem = user32.func(/* c */ `unsigned int __stdcall GetDpiForSystem()`) as () => number;
293294
const GetCursorPos = user32.func(/* c */ `bool __stdcall GetCursorPos(_Out_ POINT *lpPoint)`) as (lpPoint: Point) => boolean;
294-
const EnumDisplaySettingsA = user32.func(/* c */ `bool __stdcall EnumDisplaySettingsA(str lpszDeviceName, uint iModeNum, _Out_ DEVMODEA *lpDevMode)`) as (lpszDeviceName: string | null, iModeNum: number, lpDevMode: DeviceModeAnsi) => boolean;
295+
const EnumDisplaySettingsA = user32.func(/* c */ `bool __stdcall EnumDisplaySettingsA(str lpszDeviceName, uint iModeNum, _Out_ DEVMODEA *lpDevMode)`) as (lpszDeviceName: string | null, iModeNum: number, lpDevMode: Buffer) => boolean;
295296
// end TODO
296297

297298
const GetWindowThreadProcessId = user32.func(/* c */ `DWORD __stdcall GetWindowThreadProcessId(HWND hWnd, _Out_ LPDWORD lpdwProcessId)`) as (hWnd: HWND, lpdwProcessId: [LPDWORD | null]) => DWORD;
@@ -654,7 +655,11 @@ async function sendMouseMoveInput(args: { x: number, y: number, relative: boolea
654655
const updateInterval = 1000 / refreshRate;
655656
const iterations = Math.max(Math.floor(duration / updateInterval), 1);
656657

657-
const cursorPosition = {} as Point;
658+
const cursorPosition = {
659+
x: 0,
660+
y: 0,
661+
} satisfies Point;
662+
658663
if (GetCursorPos(cursorPosition) && iterations > 1) {
659664
if (relative) {
660665
x += cursorPosition.x;
@@ -733,15 +738,26 @@ function getResolutionScalingFactor(): number {
733738
function getScreenResolutionAndRefreshRate(): [number, number, number] {
734739
const width = GetSystemMetrics(SystemMetric.SM_CXSCREEN);
735740
const height = GetSystemMetrics(SystemMetric.SM_CYSCREEN);
741+
let refreshRate: number | null = null;
736742

737-
const deviceMode = {} as DeviceModeAnsi;
738-
EnumDisplaySettingsA(null, -1, deviceMode);
743+
const buffer = Buffer.alloc(sizeof(DEVMODEA));
744+
EnumDisplaySettingsA(null, -1, buffer);
745+
const deviceMode = { dmDisplayFrequency: buffer.readUInt32LE(120) } as DeviceModeAnsi;
746+
refreshRate = deviceMode.dmDisplayFrequency;
739747

740-
const refreshRate = deviceMode.dmDisplayFrequency;
741748
const resolution = [width, height, refreshRate] satisfies ReturnType<typeof getScreenResolutionAndRefreshRate>;
742749

743-
// @ts-expect-error temporary quick and dirty version of memoization
744-
getScreenResolutionAndRefreshRate = () => resolution;
750+
const nonMemoizedMethod = getScreenResolutionAndRefreshRate;
751+
const currentTime = new Date().getTime();
752+
753+
// @ts-expect-error memoizing the function to prevent repeated calls that might crash Node.js
754+
getScreenResolutionAndRefreshRate = () => {
755+
if (new Date().getTime() - currentTime > 1000) {
756+
// @ts-expect-error reset memoization after 1 second
757+
getScreenResolutionAndRefreshRate = nonMemoizedMethod;
758+
}
759+
return resolution;
760+
};
745761

746762
return resolution;
747763
}
@@ -774,12 +790,9 @@ export function mouseUp(button: number = 0): void {
774790
sendMouseButtonInput(button, false);
775791
}
776792

777-
export function getDisplayOrientation(): number {
778-
const deviceMode = {} as DeviceModeAnsi;
779-
EnumDisplaySettingsA(null, -1, deviceMode);
780-
781-
const orientation = deviceMode.u1.s2.dmDisplayOrientation;
782-
return orientation;
793+
export function getDisplayOrientation(): Orientation {
794+
const resolution = getScreenResolutionAndRefreshRate();
795+
return resolution[0] > resolution[1] ? 'LANDSCAPE' : 'PORTRAIT';
783796
}
784797

785798
export function setDpiAwareness() {

0 commit comments

Comments
 (0)