@@ -429,11 +429,9 @@ function makeMouseMoveEvents(args: {
429429 wheel : boolean ,
430430 /** Set to true if the event is a mouse move with relative coordinates. This argument is ignored for mouse wheel move. */
431431 relative ?: boolean ,
432- /** Set to screen resolution [width, height] when the mouse move is absolute. */
433- screenResolutionAndRefreshRate ?: ReturnType < typeof getScreenResolutionAndRefreshRate > ;
434432 }
435433) : MouseEvent [ ] {
436- const { x, y, wheel, relative, screenResolutionAndRefreshRate } = args ;
434+ const { x, y, wheel, relative } = args ;
437435
438436 if ( wheel ) {
439437 const mouseEvents : MouseEvent [ ] = [ ] ;
@@ -457,18 +455,18 @@ function makeMouseMoveEvents(args: {
457455
458456 const mouseEvent : MouseEvent = makeEmptyMouseEvent ( ) ;
459457
460- if ( ! screenResolutionAndRefreshRate ) {
461- throw new errors . InvalidArgumentError ( 'screenResolution parameter must be set for absolute mouse move.' ) ;
462- }
463-
464- const [ screenWidth , screenHeight ] = screenResolutionAndRefreshRate ;
465-
466- mouseEvent . u . mi . dx = relative ? Math . trunc ( x ) : Math . trunc ( ( x * UINT16_MAX ) / screenWidth ) ;
467- mouseEvent . u . mi . dy = relative ? Math . trunc ( y ) : Math . trunc ( ( y * UINT16_MAX ) / screenHeight ) ;
468- mouseEvent . u . mi . dwFlags = MouseEventFlags . MOUSEEVENTF_MOVE ;
469-
470- if ( ! relative ) {
471- mouseEvent . u . mi . dwFlags |= MouseEventFlags . MOUSEEVENTF_ABSOLUTE ;
458+ if ( relative ) {
459+ mouseEvent . u . mi . dx = Math . trunc ( x ) ;
460+ mouseEvent . u . mi . dy = Math . trunc ( y ) ;
461+ mouseEvent . u . mi . dwFlags = MouseEventFlags . MOUSEEVENTF_MOVE ;
462+ } else {
463+ const virt = getVirtualScreenBounds ( ) ;
464+ mouseEvent . u . mi . dx = Math . trunc ( ( ( x - virt . left ) * UINT16_MAX ) / virt . width ) ;
465+ mouseEvent . u . mi . dy = Math . trunc ( ( ( y - virt . top ) * UINT16_MAX ) / virt . height ) ;
466+ mouseEvent . u . mi . dwFlags =
467+ MouseEventFlags . MOUSEEVENTF_MOVE |
468+ MouseEventFlags . MOUSEEVENTF_ABSOLUTE |
469+ MouseEventFlags . MOUSEEVENTF_VIRTUALDESK ;
472470 }
473471
474472 return [ mouseEvent ] ;
@@ -651,8 +649,7 @@ function sendMouseButtonInput(button: number, down: boolean) {
651649async function sendMouseMoveInput ( args : { x : number , y : number , relative : boolean , duration : number , easingFunction ?: string } ) : Promise < void > {
652650 const { duration } = args ;
653651 let { x, y, easingFunction, relative } = args ;
654- const screenResolutionAndRefreshRate = getScreenResolutionAndRefreshRate ( ) ;
655- const [ , , refreshRate ] = screenResolutionAndRefreshRate ;
652+ const refreshRate = getRefreshRate ( ) ;
656653 const updateInterval = 1000 / refreshRate ;
657654 const iterations = Math . max ( Math . floor ( duration / updateInterval ) , 1 ) ;
658655
@@ -695,14 +692,14 @@ async function sendMouseMoveInput(args: { x: number, y: number, relative: boolea
695692 const interpolatedX = cursorPosition . x + ( x - cursorPosition . x ) * easedProgress ;
696693 const interpolatedY = cursorPosition . y + ( y - cursorPosition . y ) * easedProgress ;
697694
698- const events = makeMouseMoveEvents ( { x : interpolatedX , y : interpolatedY , wheel : false , screenResolutionAndRefreshRate } ) ;
695+ const events = makeMouseMoveEvents ( { x : interpolatedX , y : interpolatedY , wheel : false } ) ;
699696 const returnCode = SendInput ( events . length , events , sizeof ( INPUT ) ) ;
700697
701698 assertSuccessSendInputReturnCode ( returnCode ) ;
702699 } , i * updateInterval ) ;
703700 }
704701 } else {
705- const events = makeMouseMoveEvents ( { x, y, wheel : false , screenResolutionAndRefreshRate } ) ;
702+ const events = makeMouseMoveEvents ( { x, y, wheel : false } ) ;
706703 const returnCode = SendInput ( events . length , events , sizeof ( INPUT ) ) ;
707704
708705 assertSuccessSendInputReturnCode ( returnCode ) ;
@@ -736,33 +733,56 @@ function getResolutionScalingFactor(): number {
736733 return scalingFactor ;
737734}
738735
739- function getScreenResolutionAndRefreshRate ( ) : [ number , number , number ] {
740- const width = GetSystemMetrics ( SystemMetric . SM_CXSCREEN ) ;
741- const height = GetSystemMetrics ( SystemMetric . SM_CYSCREEN ) ;
742- let refreshRate : number | null = null ;
743-
736+ function getRefreshRate ( ) : number {
744737 const buffer = Buffer . alloc ( sizeof ( DEVMODEA ) ) ;
745738 EnumDisplaySettingsA ( null , - 1 , buffer ) ;
746- const deviceMode = { dmDisplayFrequency : buffer . readUInt32LE ( 120 ) } as DeviceModeAnsi ;
747- refreshRate = deviceMode . dmDisplayFrequency ;
739+ const refreshRate = ( buffer . readUInt32LE ( 120 ) as DeviceModeAnsi [ 'dmDisplayFrequency' ] ) ;
748740
749- const resolution = [ width , height , refreshRate ] satisfies ReturnType < typeof getScreenResolutionAndRefreshRate > ;
741+ const nonMemoizedMethod = getRefreshRate ;
742+ const currentTime = new Date ( ) . getTime ( ) ;
750743
751- const nonMemoizedMethod = getScreenResolutionAndRefreshRate ;
744+ // @ts -expect-error memoizing the function to prevent repeated calls that might crash Node.js
745+ getRefreshRate = ( ) => {
746+ if ( new Date ( ) . getTime ( ) - currentTime > 1000 ) {
747+ // @ts -expect-error reset memoization after 1 second
748+ getRefreshRate = nonMemoizedMethod ;
749+ }
750+ return refreshRate ;
751+ } ;
752+
753+ return refreshRate ;
754+ }
755+
756+ function getScreenResolution ( ) : [ number , number ] {
757+ const width = GetSystemMetrics ( SystemMetric . SM_CXSCREEN ) ;
758+ const height = GetSystemMetrics ( SystemMetric . SM_CYSCREEN ) ;
759+
760+ const resolution = [ width , height ] satisfies ReturnType < typeof getScreenResolution > ;
761+
762+ const nonMemoizedMethod = getScreenResolution ;
752763 const currentTime = new Date ( ) . getTime ( ) ;
753764
754765 // @ts -expect-error memoizing the function to prevent repeated calls that might crash Node.js
755- getScreenResolutionAndRefreshRate = ( ) => {
766+ getScreenResolution = ( ) => {
756767 if ( new Date ( ) . getTime ( ) - currentTime > 1000 ) {
757768 // @ts -expect-error reset memoization after 1 second
758- getScreenResolutionAndRefreshRate = nonMemoizedMethod ;
769+ getScreenResolution = nonMemoizedMethod ;
759770 }
760771 return resolution ;
761772 } ;
762773
763774 return resolution ;
764775}
765776
777+ export function getVirtualScreenBounds ( ) : { left : number ; top : number ; width : number ; height : number } {
778+ return {
779+ left : GetSystemMetrics ( SystemMetric . SM_XVIRTUALSCREEN ) ,
780+ top : GetSystemMetrics ( SystemMetric . SM_YVIRTUALSCREEN ) ,
781+ width : GetSystemMetrics ( SystemMetric . SM_CXVIRTUALSCREEN ) ,
782+ height : GetSystemMetrics ( SystemMetric . SM_CYVIRTUALSCREEN ) ,
783+ } ;
784+ }
785+
766786export function keyDown ( char : string , forceUnicode : boolean = false ) : void {
767787 sendKeyInput ( char , true , forceUnicode ) ;
768788}
@@ -792,7 +812,7 @@ export function mouseUp(button: number = 0): void {
792812}
793813
794814export function getDisplayOrientation ( ) : Orientation {
795- const resolution = getScreenResolutionAndRefreshRate ( ) ;
815+ const resolution = getScreenResolution ( ) ;
796816 return resolution [ 0 ] > resolution [ 1 ] ? 'LANDSCAPE' : 'PORTRAIT' ;
797817}
798818
0 commit comments