【问题标题】:How check for Command-Period in a tight loop?如何在一个紧密的循环中检查 Command-Period?
【发布时间】:2013-04-22 06:04:56
【问题描述】:

我正在实现一种脚本语言,用户可能会在其中意外导致无限循环。我想让用户有机会通过在键入句点(“.”)键的同时按住命令键来取消这种失控循环。

目前,每行一次,我使用以下代码检查是否取消:

NSEvent *   evt = [[NSApplication sharedApplication] nextEventMatchingMask: NSKeyDownMask untilDate: [NSDate date] inMode: WILDScriptExecutionEventLoopMode dequeue: YES];
if( evt )
{
    NSString        *   theKeys = [evt charactersIgnoringModifiers];
    if( (evt.modifierFlags & NSCommandKeyMask) && theKeys.length > 0 && [theKeys characterAtIndex: 0] == '.' )
    {
        // +++ cancel script execution here.
    }
}

这样做的问题是它会在脚本运行时吃掉用户可能正在键入的任何键盘事件,即使脚本应该能够检查按键。此外,它不会使相应的 NSKeyUp 事件出列。但是,如果我告诉它也将按键事件出列,它可能会将 keyUp 出列,以便在我的脚本开始之前持有的按键,并且我的应用程序可能永远不会发现按键已释放。

另外,在我知道它实际上是一个取消事件之前,我不想让任何事件出队,但是没有单独的出队调用,并且仅仅假设第二次调用中最前面的事件将是同一个事件感觉不可靠.即使它保证是第一个,这也意味着用户输入一个“a”然后 Cmd-。这意味着我只看到'a'而从来没有看到Cmd-。如果我不让事件出队,就在它后面。

有没有比使用旧的 Carbon 备用 GetKeys() 更好的选择?幸运的是,这似乎在 64 位中可用。

另外,我正在考虑添加一个 NSStatusItem,它添加一个按钮来取消脚本到菜单栏左右。但是我将如何以不允许用户的方式处理事件,例如在脚本期望成为主线程的标尺时选择菜单?

有什么建议吗?推荐?

【问题讨论】:

    标签: cocoa loops scripting


    【解决方案1】:

    按照 Dave 的建议,使用 -addLocalMonitorForEventsMatchingMask: 可能是解决此问题的最简单方法,是的。

    我只是想补充一点,尽管您感觉不可靠,但事件队列实际上是一个队列,事件不会改变顺序。调用-nextEventMatchingMask:inMode:dequeue:NO,检查事件,确定它是您要处理的事件,然后调用-nextEventMatchingMask:inMode:dequeue:YES 以使用它是非常安全的(并且是事件循环中的标准做法)。只需确保两次调用之间的掩码和模式相同即可。

    【讨论】:

    • 查看我对 Dave 的评论,了解为什么这在我的情况下不起作用。我没有处理事件,我正在运行一个脚本(我想定期查看事件队列以查看是否必须取消它)。而 nextEvent 只获取队列中的第一个事件。除非有人出队,否则我永远看不到 Cmd-。跟随它。
    【解决方案2】:

    我建议使用事件监视器。由于您向NSApp 询问事件,因此您似乎正在当前进程中运行脚本,因此您只需要监视自己进程中的事件(而不是全局)。

    有几种方法可以做到这一点(子类化NSApplication 并覆盖-sendEvent:,放入事件点击等),但最简单的方法是使用local event monitor

    id eventHandler = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDown handler:^(NSEvent *event) {
      // check the runloop mode
      // check for cmd-.
      // abort the script if necessary
    
      return event;
    }];
    

    当您完成对事件的监控后,不要忘记取消注册您的监控器:

    [NSEvent removeMonitor:eventHandler];
    

    【讨论】:

    • 很遗憾,这不起作用(我只是尝试了本地监视器)。您的建议将要求我实际将事件出列并通过 -sendEvent: 调度它们。但这意味着用户可以在脚本运行时输入文本字段等,这正是我不想要的。
    • @uliwitness 我想我不明白你想要什么。如果您想阻止事件通过-sendEvent: 发送,请从块中返回nil
    • 我正在运行脚本。这些脚本可以做其他编程语言可以做的任何事情,包括查看事件队列或处理按键操作。或者他们可能不会。我希望能够取消运行脚本,但我不想在进程中从脚本中获取合法的关键事件,除非有人实际上取消。 AppKit 似乎从 sendEvent 内部运行监视器,所以我必须调度事件才能监视它们,这意味着脚本不再看到队列中的事件。
    【解决方案3】:

    所以+[NSEvent modifierFlags] 旨在替代GetKeys()。遗憾的是,它不包括句点键的用例。

    事件队列的核心问题是您希望能够搜索它,这不是 API 公开的内容。我能想到的唯一解决方法是将所有事件出列到一个数组中,检查Command-. 事件,然后使用postEvent:atStart: 将它们全部重新排队。不漂亮。

    也许作为一种优化,您可以使用+[NSEvent modifierFlags] 仅在按住命令键时检查事件队列,但这听起来对我来说是竞争条件。

    所以最后的建议,覆盖-postEvent:atStart:(在NSApplicationNSWindow 上),看看你是否可以在那里找到所需的信息。我认为在最坏的情况下调试可能会很有趣。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 2010-11-03
      • 2011-11-26
      • 2011-04-24
      • 1970-01-01
      • 2013-06-14
      相关资源
      最近更新 更多