Daiwei Li
Daiwei Li

Reputation: 326

OSX: How to detect if Mission Control is running?

When Mission Control runs, it prevents applications from receiving keyboard and mouse events. It also leaves the last application running thinking that it still has focus. This is a problem for me because I don't receive keyUp or mouseUp events if I start Mission Control with a mouse button or a key held down and my application will behave as if that mouse button or key is held down.

I would like a way to either read both keyboard and mouse events even when Mission Control is active, or a way of detecting that Mission Control is active. Ideally, I would like to be able to do the latter since I effectively can't use my application when Mission Control is running.

I've tried a couple of things with no luck:

  1. Use addGlobalMonitorForEventsMatchingMask to register a global monitor for keyboard and mouse events. This captures mouse events (but not keyboard events, although the documentation says keyDown events should be sent to the global monitor) when I switch to another application, but Mission Control doesn't seem to let events propagate to global monitors.
  2. Check [[NSRunningApplication currentApplication] {isActive, ownsMenuBar}]. Apparently, my application is active even though it's not receiving events!
  3. Check [NSApp keyWindow] != nil. Apparently, one of my windows should be receiving key events. None of them are.
  4. Check if Mission Control is one of the running applications returned by [NSWorkspace runningApplications]. Mission Control does not show up in this list when it's running.

Edit:

I've finally worked around this problem (albeit not in a very satisfactory way). For the mouse, it turns out that you can query the state of the pressed buttons with [NSEvent pressedMouseButtons]. I simply keep track of what I think the mouse state should be from NSLeftMouseDown and NSLeftMouseUp events and compare that to [NSEvent pressedMouseButtons] every so often to make sure that they're consistent. If they're not, then I know that something has hijacked my NSLeftMouseUp event and act accordingly.

For the keyboard, I could not find a way to query the keyboard state, so I couldn't do a similar workaround. I ended up disabling application switching using presentation options when keys are pressed.

Upvotes: 27

Views: 3005

Answers (5)

Valentino
Valentino

Reputation: 21

C++ and Qt implementation works in latest 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;
}

Upvotes: 0

Tim
Tim

Reputation: 1689

At least in OS X 10.10, you can use this code to check if Mission Control is active or not:

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
}

In a nutshell, the code checks if a specific window owned by the OS X Dock process is visible on the screen and if it is in a specific position. If both conditions are met, Mission Control will be active right now. Code will work in a sandboxed app and no privileges for assistive devices are required.

Upvotes: 4

ericg
ericg

Reputation: 8722

It does appear that DTrace has some ability to see Mission Control being activated. Try running:

sudo fs_usage -filesys | grep Mission

from the command line and then launching the Mission Control app from the /Application folder.

You should see a lot of output related to Mission Control starting up. Unfortunately, this same output did not appear by using the keyboard short cut or swiping. Of course, using DTrace in production code is not something I would actually recommend.

Upvotes: 0

tttthomasssss
tttthomasssss

Reputation: 5971

Did you try on bash level using NSTask? Something like ps -faxU <username> should list all running processes and then you could parse the output, or indeed you could use ps -faxU <username> | grep -i "mission control" (At the top of my head I am not sure how the process may be called, but sth like "mission control" seems legit). Not the most elegant solution maybe, but if nothing else works it may be worth it.

Upvotes: 1

Hofi
Hofi

Reputation: 966

May be i'm missing something, but have you tried to use event taps instead of global monitoring?

Upvotes: 0

Related Questions