1- using System . Diagnostics ;
1+ using System . Diagnostics ;
22using System . Runtime . InteropServices ;
33using Microsoft . Extensions . Logging ;
44
@@ -17,6 +17,9 @@ public class GlobalKeyboardHook : IDisposable
1717 private const int VK_P = 0x50 ; // 80 - 'P' key for pen tool
1818 private const int VK_E = 0x45 ; // 69 - 'E' key for eraser tool
1919 private const int VK_F1 = 0x70 ; // 112 - 'F1' key for help
20+ private const int VK_S = 0x53 ; // 83 - 'S' key for screenshot (Ctrl+S only)
21+ private const int VK_LCONTROL = 0xA2 ; // 162 - Left Control key
22+ private const int VK_RCONTROL = 0xA3 ; // 163 - Right Control key
2023
2124 private readonly ILogger < GlobalKeyboardHook > _logger ;
2225 private readonly LowLevelKeyboardProc _proc ;
@@ -33,6 +36,7 @@ public class GlobalKeyboardHook : IDisposable
3336 public event EventHandler ? LineToolPressed ;
3437 public event EventHandler ? EraserToolPressed ;
3538 public event EventHandler ? HelpPressed ;
39+ public event EventHandler ? ScreenshotFullPressed ;
3640
3741 // NEW: Raw key events for recorder
3842 public event EventHandler < KeyEventArgs > ? KeyPressed ;
@@ -42,6 +46,10 @@ public class GlobalKeyboardHook : IDisposable
4246 private List < int > _hotkeyVKs = new ( ) { 0xA2 , 0xA4 , 0x44 } ; // Default: Ctrl+Alt+D
4347 private Dictionary < int , bool > _keyStates = new ( ) ;
4448 private bool _wasHotkeyActive = false ;
49+ private volatile bool _isControlPressed = false ;
50+
51+ // Drawing mode state - used to determine if we should suppress keys
52+ private volatile bool _isDrawingModeActive = false ;
4553
4654 public GlobalKeyboardHook ( ILogger < GlobalKeyboardHook > logger )
4755 {
@@ -53,7 +61,7 @@ public GlobalKeyboardHook(ILogger<GlobalKeyboardHook> logger)
5361 foreach ( var vk in _hotkeyVKs )
5462 _keyStates [ vk ] = false ;
5563 }
56-
64+
5765 /// <summary>
5866 /// Configures the hotkey combination
5967 /// </summary>
@@ -171,6 +179,8 @@ private nint SetHook(LowLevelKeyboardProc proc)
171179
172180 private nint HookCallback ( int nCode , nint wParam , nint lParam )
173181 {
182+ bool shouldSuppressKey = false ;
183+
174184 try
175185 {
176186 if ( nCode >= 0 )
@@ -184,10 +194,19 @@ private nint HookCallback(int nCode, nint wParam, nint lParam)
184194 else
185195 KeyReleased ? . Invoke ( this , new KeyEventArgs ( vkCode ) ) ;
186196
197+ // Track Control key state (both left and right control keys)
198+ if ( vkCode == VK_LCONTROL || vkCode == VK_RCONTROL )
199+ {
200+ _isControlPressed = isKeyDown ;
201+ _logger . LogDebug ( "Control key ({Type}) {State}" ,
202+ vkCode == VK_LCONTROL ? "Left" : "Right" ,
203+ isKeyDown ? "PRESSED" : "RELEASED" ) ;
204+ }
205+
187206 // Check for ESC key press (emergency exit)
188207 if ( vkCode == VK_ESCAPE && isKeyDown )
189208 {
190- _logger . LogInformation ( "?? ESC pressed - emergency exit" ) ;
209+ _logger . LogInformation ( "🔴 ESC pressed - emergency exit" ) ;
191210 EscapePressed ? . Invoke ( this , EventArgs . Empty ) ;
192211 }
193212
@@ -226,6 +245,32 @@ private nint HookCallback(int nCode, nint wParam, nint lParam)
226245 HelpPressed ? . Invoke ( this , EventArgs . Empty ) ;
227246 }
228247
248+ // Check for Ctrl+S key press (full screenshot only - no snipping tool)
249+ if ( vkCode == VK_S && isKeyDown && _isControlPressed )
250+ {
251+ _logger . LogInformation ( "====== CTRL+S DETECTED ======" ) ;
252+ _logger . LogInformation ( "Control key state: {IsControlPressed}" , _isControlPressed ) ;
253+ _logger . LogInformation ( "Drawing mode active: {IsDrawingModeActive}" , _isDrawingModeActive ) ;
254+
255+ _logger . LogInformation ( "Ctrl+S pressed - firing ScreenshotFullPressed event" ) ;
256+ ScreenshotFullPressed ? . Invoke ( this , EventArgs . Empty ) ;
257+ _logger . LogInformation ( "ScreenshotFullPressed event fired, subscribers: {Count}" ,
258+ ScreenshotFullPressed ? . GetInvocationList ( ) . Length ?? 0 ) ;
259+
260+ // Suppress Ctrl+S when drawing mode is active to prevent Windows Snipping Tool
261+ if ( _isDrawingModeActive )
262+ {
263+ shouldSuppressKey = true ;
264+ _logger . LogInformation ( "KEY WILL BE SUPPRESSED - Drawing mode is active" ) ;
265+ }
266+ else
267+ {
268+ _logger . LogInformation ( "KEY WILL NOT BE SUPPRESSED - Drawing mode is inactive" ) ;
269+ }
270+
271+ _logger . LogInformation ( "====== END CTRL+S HANDLING ======" ) ;
272+ }
273+
229274 // Track hotkey state
230275 if ( _hotkeyVKs . Contains ( vkCode ) )
231276 {
@@ -239,12 +284,12 @@ private nint HookCallback(int nCode, nint wParam, nint lParam)
239284 // Fire events on state changes
240285 if ( allPressed && ! _wasHotkeyActive )
241286 {
242- _logger . LogInformation ( "?? HOTKEY PRESSED" ) ;
287+ _logger . LogInformation ( "🟢 HOTKEY PRESSED" ) ;
243288 HotkeyPressed ? . Invoke ( this , EventArgs . Empty ) ;
244289 }
245290 else if ( ! allPressed && _wasHotkeyActive )
246291 {
247- _logger . LogInformation ( "?? HOTKEY RELEASED" ) ;
292+ _logger . LogInformation ( "🟢 HOTKEY RELEASED" ) ;
248293 HotkeyReleased ? . Invoke ( this , EventArgs . Empty ) ;
249294 }
250295
@@ -258,7 +303,15 @@ private nint HookCallback(int nCode, nint wParam, nint lParam)
258303 _logger . LogError ( ex , "Exception in keyboard hook callback" ) ;
259304 }
260305
261- // MUST ALWAYS call CallNextHookEx to allow other applications to process the hook
306+ // If we want to suppress the key, return 1 to block it from reaching other applications
307+ // Otherwise, call CallNextHookEx to allow other applications to process the hook
308+ if ( shouldSuppressKey )
309+ {
310+ _logger . LogTrace ( "Key suppressed - not calling CallNextHookEx" ) ;
311+ return ( nint ) 1 ;
312+ }
313+
314+ // MUST call CallNextHookEx for non-suppressed keys to allow other applications to process them
262315 return CallNextHookEx ( _hookID , nCode , wParam , lParam ) ;
263316 }
264317
@@ -280,6 +333,27 @@ public void Dispose()
280333 }
281334 }
282335
336+ /// <summary>
337+ /// Sets the drawing mode state. When active, certain keys (like Ctrl+S) will be suppressed
338+ /// to prevent Windows from intercepting them.
339+ /// </summary>
340+ public void SetDrawingModeActive ( bool isActive )
341+ {
342+ var previousState = _isDrawingModeActive ;
343+ _isDrawingModeActive = isActive ;
344+
345+ if ( previousState != isActive )
346+ {
347+ _logger . LogInformation ( "====== DRAWING MODE STATE CHANGED ======" ) ;
348+ _logger . LogInformation ( "Previous state: {PreviousState}, New state: {NewState}" , previousState , isActive ) ;
349+ _logger . LogInformation ( "Timestamp: {Timestamp}" , DateTime . Now . ToString ( "HH:mm:ss.fff" ) ) ;
350+ }
351+ else
352+ {
353+ _logger . LogDebug ( "Drawing mode state set to: {IsActive} (no change)" , isActive ) ;
354+ }
355+ }
356+
283357 // P/Invoke declarations
284358 private delegate nint LowLevelKeyboardProc ( int nCode , nint wParam , nint lParam ) ;
285359
0 commit comments