Canceling Long Processes with ESC
Windows only
Overview
When writing commands that contain long, time-consuming tasks, you might want to allow the user to cancel the process or command. Here are a couple examples of ways this can be achieved…
Example 1
The following example demonstrates how to periodically test to see if the user pressed the ESC key by “peeking” in Rhino’s message queue…
// -1 = Quit Rhino // 1 = Escape key pressed // 0 = Okay to proceed int EscapeKeyPressed() { AFX_MANAGE_STATE( RhinoApp().RhinoModuleState() ); MSG msg; memset( &msg, 0, sizeof(MSG) ); while( ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) ) { if( msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE ) return 1; if( !RhinoApp().PumpMessage() ) return -1; } return 0; }
Using this function is quite simple: in the middle of your loop, call the above function. If the function returns something other than 0, break out of your loop.
Example 2
The following sample class demonstrates how to hook the Windows keyboard from a Rhino plugin and check for the ESC key.
// // rhinoEscapeKey.h // class CRhinoEscapeKey { public: CRhinoEscapeKey( bool bHookNow = false ); ~CRhinoEscapeKey(); bool Start(); void Stop(); bool EscapeKeyPressed() const; void ClearEscapeKeyPressedFlag(); protected: static LRESULT CALLBACK HookProc( int code, WPARAM wParam, LPARAM lParam ); static HHOOK m_KeyboardHookProc; static bool m_escape_pressed; }; // // rhinoEscapeKey.cpp // bool CRhinoEscapeKey::m_escape_pressed = false; HHOOK CRhinoEscapeKey::m_KeyboardHookProc = NULL; CRhinoEscapeKey::CRhinoEscapeKey( bool bStartNow ) { if( bStartNow ) Start(); } CRhinoEscapeKey::~CRhinoEscapeKey() { Stop(); } bool CRhinoEscapeKey::Start() { if( NULL == m_KeyboardHookProc ) m_KeyboardHookProc = ::SetWindowsHookEx( WH_KEYBOARD, CRhinoEscapeKey::HookProc, RhinoApp().RhinoInstanceHandle(), ::AfxGetThread()->m_nThreadID ); ClearEscapeKeyPressedFlag(); return( NULL != m_KeyboardHookProc ); } void CRhinoEscapeKey::Stop() { if( m_KeyboardHookProc ) UnhookWindowsHookEx( m_KeyboardHookProc ); m_KeyboardHookProc = NULL; } bool CRhinoEscapeKey::EscapeKeyPressed() const { RhinoApp().Wait(0); return m_escape_pressed; } void CRhinoEscapeKey::ClearEscapeKeyPressedFlag() { m_escape_pressed = false; } LRESULT CALLBACK CRhinoEscapeKey::HookProc( int code, WPARAM wParam, LPARAM lParam ) { // On escape key down.... if( code == HC_ACTION && wParam == VK_ESCAPE && !(lParam & 0x80000000) ) { m_escape_pressed = true; UnhookWindowsHookEx( m_KeyboardHookProc ); m_KeyboardHookProc = NULL; return 0; // Eat the escape key } // call next hook proc including standard windows proc. return CallNextHookEx( m_KeyboardHookProc, code, wParam, lParam ); }
Usage
The following sample code demonstrates using the CRhinoEscapeKey
class within a Rhino command…
CRhinoCommand::result CCommandTest::RunCommand( const CRhinoCommandContext& context ) { CRhinoEscapeKey escape; escape.Start(); int i = 0; while( true ) { if( escape.EscapeKeyPressed() ) { escape.Stop(); RhinoApp().Print( L"Command canceled.\n" ); break; } RhinoApp().Print( L"Count = %d.\n", ++i ); } return success; }