RhinoApp.EscapeKeyPressed event, unable to cancel/consume keypress event


(Menno Deij - van Rijswijk) #1

I’m trying to use the RhinoApp.EscapeKeyPressed event to cancel a background worker that is running inside a command. What I find, however, is that my command gets canceled after the Esc key is pressed (which also stops the background worker).

Any ideas how to cancel out of a long running background task but keep the command itself alive? It seems a possible solution would be to be able to consume the EscKeyPressed event and not let it propagate further, similar to how mouse events are handled (by setting Rhino.UI.MouseCallbackEventArgs.Cancel = true)

private BackgroundWorker _bg;
protected Result RunCommand(RhinoDoc doc, RunMode mode)
{
    int n = 10000;
    GetObject go = new GetObject();
    go.AcceptNothing(true);
    OptionInteger optN = new OptionInteger(n);
    go.AddOptionInteger("Cycles", ref optN);

    RhinoApp.EscapeKeyPressed += OnEscPressed;
                    
    while (true)
    {
        _bg = new BackgroundWorker();
        _bg.WorkerSupportsCancellation = true;
        _bg.WorkerReportsProgress = false;
        _bg.DoWork += (o, a) =>
        {
            BackgroundWorker bgw = o as BackgroundWorker;
            DoWork(bgw, a, optN.CurrentValue);
        };

        RhinoApp.WriteLine("Running background task. Press Esc to cancel...");
        RhinoApp.WriteLine();
        _bg.RunWorkerAsync();
        while (_bg.IsBusy)
        {
            RhinoApp.Wait();
        }
        RhinoApp.WriteLine(" ... background task completed");
        RhinoApp.WriteLine();

        GetResult res = go.Get();
        if (res == GetResult.Cancel)
        {
            RhinoApp.WriteLine("Cancelling command");
            return Result.Cancel;
        }
        if (res == GetResult.Nothing)
            break;

    }

    RhinoApp.EscapeKeyPressed -= OnEscPressed;
                    

    RhinoApp.WriteLine("Command successfully completed");
    return Result.Success;
            
}

private void OnEscPressed(object sender, EventArgs args)
{
    if (null != _bg)
    {
        RhinoApp.WriteLine("Cancelling worker");
        _bg.CancelAsync();
    }
}

private void DoWork(BackgroundWorker bg, DoWorkEventArgs args, int n)
{
    // let's keep the processor busy.
    int r = 0;
    for (int i = 0; !bg.CancellationPending && i < n; ++i)
    {
        r = 0;
        for (int j = 0; !bg.CancellationPending && j < n; ++j)
        {
            r++;
        }
    }
    RhinoApp.WriteLine("End of DoWork. CancellationPending = {0}", bg.CancellationPending);
}

(Menno Deij - van Rijswijk) #2

FWIW, I coded a key listener in the past that I now used to solve this problem. The listener can cancel the event and prevent it from progressing. It would be nice if RhinoApp.EscapeKeyPressed could also do this.


(Dale Fugier) #3

Hi Menno,

RhinoApp.EscapeKeyPressed hooks the keyboard using the WH_KEYBOARD hook. This hook enables you to monitory WM_KEYDOWN and WM_KEYUP messages about to be returned by the GetMessage or PeekMessage (e.g. Rhino’s message queue). This why your command is being cancelled.

The advantage of using the WH_KEYBOARD_LL hook, as you’ve experienced, is that you are notified before the message is posted. There are some ramifications when using this, such as whether or not to call the next hook proc.

Since RhinoApp.EscapeKeyPressed is unique to RhinoCommon, we might be able to make a change, but probably not until V6. Let me discuss this is @stevebaer


(Steve Baer) #4

I’m open to changing this, but it probably should only be done in V6 since it is a significant change.