How to get if an application has focus on macOS?

白昼怎懂夜的黑 提交于 2021-01-05 08:54:43

问题


I have the need to gather which application has focus. For this, my approach is to: list windows, get the one with focus, and finally, check which process and application shows it. If there were some: getWindowWithFocus(), it would be fantastic.

Requirements:

  • The program is implemented in C++, but could interface with objective-C if needed.
  • The program will run with root privileges.
  • The list of windows listed must include all users applications.
  • The returned window allows to get properties, such as it process and if it has UI-focus.
  • Ideally, no 3rd party tool is used, only standard libraries (STL, Unix APIs and macOS APIs, eventually Qt/Boost).
  • Must support HSierra to Big-Sur.

I managed to list all windows, but now I am struggling in detecting if a window has or not the focus.

The question:

  • Which API function can be used to check if a window has focus or not? Any sample?
  • Any better approach to this problem?

Previous research:

I created a POC/sample which list all windows, including some of it properties.

CGWindowListCopyWindowInfo

https://developer.apple.com/documentation/coregraphics/1455137-cgwindowlistcopywindowinfo?language=objc

DISCLAIM: this is a POC, just for demonstration, and miss required code quality for proper projects. For example, CFObjects are not released with the consequent memory leak.

#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CGWindow.h> // CoreGraphics 
#include <iostream>

int main()
{
    CFArrayRef ref = CGWindowListCopyWindowInfo(kCGNullWindowID, 0);
    
    CFIndex nameCount = CFArrayGetCount( ref );
    
    std::cout << "NumCounts: " << nameCount << " windows" << std::endl;
    
    for( int i = 0; i < nameCount ; ++i  )
    {
        std::cerr << " -------- " << std::endl;
        CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex( ref, i );
        
        auto printKeys = [](const void* key, const void* value, void* context) 
        {
            CFShow(key);
            std::cerr << "    ";
            CFShow(value);
        };
        
        CFDictionaryApplyFunction(dict, printKeys, nullptr);

        // Process PID can be extracted with key:kCGWindowOwnerPID
        // DOES THIS WINDOW HAS FOCUS?
    }
}

回答1:


Here is an example, based on this solution, wrapped in C++ (well, actually mostly C).

The only found problem with it is, it must run in main thread, which is not convenient, but this is another topic.

main.cpp:

#include "focus_oc_wrapper.hpp"
#include <thread>
        
int main(int argc, const char * argv[])
{
    FocusDetector::AppFocus focus;
    focus.run();

    //std::thread threadListener(&FocusDetector::AppFocus::run, &focus); //Does not works
    //if (threadListener.joinable())
    //{
    //  threadListener.join();
    //}
}

focus_oc_wrapper.hpp

namespace FocusDetector
{
    struct AppFocusImpl;
    struct AppFocus
    {
        AppFocusImpl* impl=nullptr;
        AppFocus() noexcept;
        ~AppFocus();
        void run();
    };
}

focus_oc_wrapper.mm

#include "focus_oc_wrapper.hpp"

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "focus_oc.h"

namespace FocusDetector
{

struct AppFocusImpl
{
    OCAppFocus* wrapped=nullptr;
};

AppFocus::AppFocus() noexcept: impl(new AppFocusImpl)
{
    impl->wrapped = [[OCAppFocus alloc] init];
}

AppFocus::~AppFocus()
{
    if (impl)
    {
        [impl->wrapped release];
    }
    delete impl;
}

void AppFocus::run()
{
    [NSApplication sharedApplication];
    [NSApp setDelegate:impl->wrapped];
    [NSApp run];
}

}

focus_oc.h

#import <Foundation/Foundation.h>

@interface OCAppFocus : NSObject <NSApplicationDelegate> 
{
    NSRunningApplication    *currentApp;
}
@property (retain) NSRunningApplication *currentApp;
@end

@implementation OCAppFocus 
@synthesize currentApp;

- (id)init 
{
    if ((self = [super init])) 
    {
        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                      selector:@selector(activeAppDidChange:)
               name:NSWorkspaceDidActivateApplicationNotification object:nil];
    }
    return self;
}
- (void)dealloc 
{
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
    [super dealloc];
}
- (void)activeAppDidChange:(NSNotification *)notification 
{
    self.currentApp = [[notification userInfo] objectForKey:NSWorkspaceApplicationKey];
    
    NSLog(@"App:      %@", [currentApp localizedName]);
    NSLog(@"Bundle:   %@", [currentApp bundleIdentifier]);
    NSLog(@"Exec Url: %@", [currentApp executableURL]);
    NSLog(@"PID:      %d", [currentApp processIdentifier]);
}
@end

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version")

project("focus_detection")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreFoundation -framework AppKit")
set ( TESTCPP main.cpp focus_oc_wrapper.mm )

add_executable( ${PROJECT_NAME} ${TESTCPP} ) 


来源:https://stackoverflow.com/questions/64896395/how-to-get-if-an-application-has-focus-on-macos

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!