33using System . Linq ;
44using System . Runtime . InteropServices ;
55using System . Threading ;
6+ using System . Threading . Tasks ;
67using AutoMidiPlayer . Data . Entities ;
78using WindowsInput ;
89using WindowsInput . Native ;
@@ -67,7 +68,8 @@ private struct KEYBDINPUT
6768 /// Delay (in milliseconds) inserted between key-down and key-up when performing
6869 /// <see cref="KeyAction.Press"/> in InputSimulator, direct input, and window-message paths.
6970 /// A non-zero value can improve compatibility with games that require a
70- /// slightly longer held press. Default is 0.
71+ /// slightly longer held press. Delay is handled asynchronously so playback timing
72+ /// is not blocked. Default is 0.
7173 /// </summary>
7274 public static int DirectInputPressDelayMs { get ; set ; } = 0 ;
7375
@@ -196,12 +198,21 @@ private static void SendKeyStrokeSimulated(Keyboard.KeyStroke keyStroke, KeyActi
196198 foreach ( var modifier in modifiers )
197199 Input . Keyboard . KeyDown ( modifier ) ;
198200 Input . Keyboard . KeyDown ( keyStroke . Key ) ;
199- if ( DirectInputPressDelayMs > 0 )
200- Thread . Sleep ( DirectInputPressDelayMs ) ;
201+
201202 if ( EnableKeyUp )
202- Input . Keyboard . KeyUp ( keyStroke . Key ) ;
203- for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
204- Input . Keyboard . KeyUp ( modifiers [ i ] ) ;
203+ {
204+ ScheduleDelayedAction ( ( ) =>
205+ {
206+ Input . Keyboard . KeyUp ( keyStroke . Key ) ;
207+ for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
208+ Input . Keyboard . KeyUp ( modifiers [ i ] ) ;
209+ } ) ;
210+ }
211+ else
212+ {
213+ for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
214+ Input . Keyboard . KeyUp ( modifiers [ i ] ) ;
215+ }
205216 break ;
206217 }
207218 }
@@ -228,13 +239,21 @@ private static void SendKeyStrokeDirect(Keyboard.KeyStroke keyStroke, KeyAction
228239 foreach ( var modifier in modifiers )
229240 SendKeyDirect ( modifier , false ) ;
230241 SendKeyDirect ( keyStroke . Key , false ) ;
231- // Add custom delay between key-down and key-up to improve compatibility with games
232- if ( DirectInputPressDelayMs > 0 )
233- Thread . Sleep ( DirectInputPressDelayMs ) ;
242+
234243 if ( EnableKeyUp )
235- SendKeyDirect ( keyStroke . Key , true ) ;
236- for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
237- SendKeyDirect ( modifiers [ i ] , true ) ;
244+ {
245+ ScheduleDelayedAction ( ( ) =>
246+ {
247+ SendKeyDirect ( keyStroke . Key , true ) ;
248+ for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
249+ SendKeyDirect ( modifiers [ i ] , true ) ;
250+ } ) ;
251+ }
252+ else
253+ {
254+ for ( int i = modifiers . Length - 1 ; i >= 0 ; i -- )
255+ SendKeyDirect ( modifiers [ i ] , true ) ;
256+ }
238257 break ;
239258 }
240259 }
@@ -293,11 +312,38 @@ private static void SendKeyStrokeWindow(Keyboard.KeyStroke keyStroke, IntPtr hWn
293312
294313 case KeyAction . Press :
295314 PostMessage ( hWnd , WM_KEYDOWN , ( IntPtr ) vk , lParamDown ) ;
296- if ( DirectInputPressDelayMs > 0 )
297- Thread . Sleep ( DirectInputPressDelayMs ) ;
298315 if ( EnableKeyUp )
299- PostMessage ( hWnd , WM_KEYUP , ( IntPtr ) vk , lParamUp ) ;
316+ {
317+ ScheduleDelayedAction ( ( ) =>
318+ {
319+ PostMessage ( hWnd , WM_KEYUP , ( IntPtr ) vk , lParamUp ) ;
320+ } ) ;
321+ }
300322 break ;
301323 }
302324 }
325+
326+ private static void ScheduleDelayedAction ( Action action )
327+ {
328+ var delayMs = Math . Max ( 0 , DirectInputPressDelayMs ) ;
329+
330+ if ( delayMs == 0 )
331+ {
332+ action ( ) ;
333+ return ;
334+ }
335+
336+ _ = Task . Run ( async ( ) =>
337+ {
338+ try
339+ {
340+ await Task . Delay ( delayMs ) . ConfigureAwait ( false ) ;
341+ action ( ) ;
342+ }
343+ catch
344+ {
345+ // Best-effort delayed key-up dispatch.
346+ }
347+ } ) ;
348+ }
303349}
0 commit comments