【问题标题】:OSX: How to detect if Mission Control is running?OSX:如何检测 Mission Control 是否正在运行?
【发布时间】:2012-09-22 21:20:55
【问题描述】:

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

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

我尝试了一些没有运气的事情:

  1. 使用 addGlobalMonitorForEventsMatchingMask 为键盘和鼠标事件注册一个全局监视器。当我切换到另一个应用程序时,这会捕获鼠标事件(但不是键盘事件,尽管documentation 说 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 事件并采取相应措施。

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

【问题讨论】:

  • 来自文档:“只有在启用了可访问性或您的应用程序受信任以进行可访问性访问时,才能监控与密钥相关的事件(请参阅 AXIsProcessTrusted)。” – 这可能就是您没有收到关键事件的原因。在系统的可访问性首选项中启用“辅助设备访问”,看看是否有任何改变。
  • 你说得对,启用可访问性确实给了我全局关键事件,但不是在任务控制处于活动状态时。
  • 我建议您提交错误报告:developer.apple.com/bugreporter -另外,最好让支持者想出解决方案,而不是要求特定的东西。也就是说,错误报告应该只清楚简洁地确定问题。
  • 谢谢,我已经向 Apple 提交了错误报告。如果我从他们那里得到解决方案,我会报告。
  • 另请参阅这个非常相关的问题:stackoverflow.com/questions/9352939/…

标签: objective-c macos cocoa


【解决方案1】:

至少在 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 进程拥有的特定窗口是否在屏幕上可见,以及它是否位于特定位置。如果满足这两个条件,任务控制将立即激活。代码将在沙盒应用中运行,并且不需要辅助设备的权限。

【讨论】:

  • FWIW,这在 10.11 中对我不起作用(假设我从 Swift 正确翻译为 Obj-C)。但是,调用 Mission Control 确实会导致额外的条目以“Dock”作为所有者名称,因此可能有另一种方法来解析它们以确定 MC 是否确实是导致这种情况的原因。现在必须留下问题,但如果我弄清楚了会更新......
【解决方案2】:

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

【讨论】:

  • 我在终端上试过了。不幸的是,Mission Control 存在的时间很短,因为它所做的只是向 Dock 进程发送一条 mach 消息(请参阅link),因此您永远不会在正在运行的进程列表中真正看到它处于活动状态。收听 NSWorkspace 通知告诉您应用程序何时出现,也没有显示任务控制的痕迹。由于其他原因,this page 上的其他选项对我不适用。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    sudo fs_usage -filesys | grep 任务

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

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

    【讨论】:

      【解决方案5】:

      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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-28
        • 1970-01-01
        • 1970-01-01
        • 2016-04-25
        相关资源
        最近更新 更多