26

当 Mission Control 运行时,它会阻止应用程序接收键盘和鼠标事件。它还使最后一个运行的应用程序认为它仍然具有焦点。这对我来说是个问题,因为如果我用鼠标按钮或按住的键启动任务控制,我不会收到 keyUp 或 mouseUp 事件,并且我的应用程序的行为就像按住鼠标按钮或键一样。

我想要一种即使在任务控制处于活动状态时也能读取键盘和鼠标事件的方法,或者一种检测任务控制处于活动状态的方法。理想情况下,我希望能够执行后者,因为在 Mission Control 运行时我实际上无法使用我的应用程序。

我尝试了几件事没有运气:

  1. 使用 addGlobalMonitorForEventsMatchingMask 为键盘和鼠标事件注册一个全局监视器。当我切换到另一个应用程序时,这会捕获鼠标事件(但不是键盘事件,尽管文档说 keyDown 事件应该被发送到全局监视器),但是 Mission Control 似乎并没有让事件传播到全局监视器。
  2. 检查[[NSRunningApplication currentApplication] {isActive, ownsMenuBar}]。显然,我的应用程序处于活动状态,即使它没有收到事件!
  3. 检查[NSApp keyWindow] != nil。显然,我的一个窗口应该正在接收关键事件。他们都不是。
  4. 检查 Mission Control 是否是[NSWorkspace runningApplications]. Mission Control 在运行时不会显示在此列表中。

编辑:

我终于解决了这个问题(尽管不是很满意)。对于鼠标,事实证明您可以使用 查询按下按钮的状态[NSEvent pressedMouseButtons]。我只是从 NSLeftMouseDown 和 NSLeftMouseUp 事件中跟踪我认为鼠标状态应该是什么,并将其与[NSEvent pressedMouseButtons]每隔一段时间进行比较以确保它们是一致的。如果不是,那么我知道某些东西劫持了我的 NSLeftMouseUp 事件并采取相应措施。

对于键盘,我找不到查询键盘状态的方法,所以我无法做类似的解决方法。我最终在按下按键时使用演示选项禁用了应用程序切换。

4

5 回答 5

3

至少在 OS X 10.10 中,您可以使用此代码检查 Mission Control 是否处于活动状态:

func missionControlIsActive() -> Bool
{
    var result: Bool = false
    let windowInfosRef = CGWindowListCopyWindowInfo(CGWindowListOption(kCGWindowListOptionOnScreenOnly), CGWindowID(0)) // CGWindowID(0) is equal to kCGNullWindowID
    let windowList: NSArray = windowInfosRef.takeRetainedValue() // We own the returned CFArrayRef
    for entry in windowList
    {
        if (entry.objectForKey("kCGWindowOwnerName") as! String) == "Dock"
        {
            var bounds: NSDictionary = entry.objectForKey("kCGWindowBounds") as! NSDictionary
            if (bounds.objectForKey("Y") as! NSNumber) == -1
            {
                result = true
            }
        }
    }
    return result
}

简而言之,代码检查 OS X Dock 进程拥有的特定窗口是否在屏幕上可见,以及它是否位于特定位置。如果满足这两个条件,任务控制将立即激活。代码将在沙盒应用程序中运行,并且不需要辅助设备的权限。

于 2015-07-27T23:42:23.560 回答
1

您是否使用 NSTask 尝试过 bash 级别?ps -faxU <username>应该列出所有正在运行的进程,然后您可以解析输出,或者确实可以使用ps -faxU <username> | grep -i "mission control"(在我的脑海中,我不确定如何调用该进程,但是诸如“任务控制”之类的东西似乎是合法的)。也许不是最优雅的解决方案,但如果没有其他办法,它可能是值得的。

于 2012-10-29T22:08:17.303 回答
0

可能是我遗漏了一些东西,但是您是否尝试过使用事件点击而不是全局监控?

于 2012-10-25T21:01:05.750 回答
0

看起来 DTrace 确实有能力看到任务控制被激活。尝试运行:

须藤 fs_usage -filesys | grep 任务

从命令行,然后从 /Application 文件夹启动 Mission Control 应用程序。

您应该会看到很多与 Mission Control 启动相关的输出。不幸的是,使用键盘快捷方式或轻扫没有出现相同的输出。当然,我实际上并不推荐在生产代码中使用 DTrace。

于 2013-03-02T20:58:36.343 回答
0

C++ 和 Qt 实现适用于最新的 OS X。

bool Window::missionControlIsActive() {
    bool result = false;
    CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly+kCGWindowListExcludeDesktopElements, kCGNullWindowID);
    for (int i = 0; i < CFArrayGetCount(windows) ; i++) {
        auto cfMutableDictionaryRef_dict    = (CFMutableDictionaryRef)CFArrayGetValueAtIndex( windows, i );
        auto cfStringRef_name = (CFStringRef)CFDictionaryGetValue(cfMutableDictionaryRef_dict, kCGWindowName);
        if (QString::fromCFString(cfStringRef_name) != u"") continue;
        auto cfStringRef_ownerName = (CFStringRef)CFDictionaryGetValue(cfMutableDictionaryRef_dict, kCGWindowOwnerName);
        if (QString::fromCFString(cfStringRef_ownerName) != u"Dock") continue;
        auto cfDictRef_bounds = (CFDictionaryRef)CFDictionaryGetValue(cfMutableDictionaryRef_dict, kCGWindowBounds);
        auto cfNumRef_bounds_Y = (CFNumberRef)CFDictionaryGetValue(cfDictRef_bounds, QString("Y").toCFString());
        double num;
        CFNumberGetValue(cfNumRef_bounds_Y, kCFNumberFloat64Type, &num);
        if (num > 1.0 and num < 1000000) continue;
        result = true;
        break;
    }
    CFRelease(windows);
    return result;
}
于 2021-03-14T20:12:42.880 回答