c++ - 如何获取应用程序是否专注于 macOS?

标签 c++ macos focus

我需要收集哪个应用程序具有焦点。为此,我的方法是:列出窗口,获取具有焦点的窗口,最后检查哪个进程和应用程序显示它。如果有一些:getWindowWithFocus(),那就太棒了。
要求:

  • 该程序是用 C++ 实现的,但如果需要,可以与 Objective-C 接口(interface)。
  • 该程序将以 root 权限运行。
  • 列出的窗口列表必须包括 全部 用户应用程序。
  • 返回的窗口允许获取属性,例如它的进程以及它是否具有 UI 焦点。
  • 理想情况下,不使用第三方工具,只使用标准库(STL、Unix API 和 macOS API,最终是 Qt/Boost)。
  • 必须支持 HSierra 到 Big-Sur。

  • 我设法列出了所有窗口,但现在我正在努力检测一个窗口是否有焦点。
    问题:
  • 哪个 API 函数可以用来检查窗口是否有焦点?有 sample 吗?
  • 有什么更好的方法来解决这个问题?

  • 以前的研究:
    我创建了一个 POC/示例,其中列出了所有窗口,包括其中的一些属性。
    CGWindowListCopyWindowInfo
    
    https://developer.apple.com/documentation/coregraphics/1455137-cgwindowlistcopywindowinfo?language=objc
    免责声明:这是一个 POC,仅用于演示,并且缺少正确项目所需的代码质量。例如,CFObjects 不会随着随之而来的内存泄漏而被释放。
    #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?
        }
    }
    

    最佳答案

    这是一个示例,基于 this solution ,包装在 C++ 中(嗯,实际上主要是 C)。
    唯一发现的问题是,它必须在主线程中运行,不方便,但这是另一个话题。
    主.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} ) 
    

    关于c++ - 如何获取应用程序是否专注于 macOS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64896395/

    相关文章:

    c++ - C++ (macOS) 的编译错误

    android - 输入编辑文本时焦点被移除

    c++ - ‘string’ does not name a type error"in eclipse cdt

    c++ - 创建动态二维数组的其他方法?

    c++ - 如何使用 OpenCV 降低图像的每像素位数

    macos - CGEventPost - 模拟键盘事件时可能的错误?

    macos - Mac 的 cmake 安装程序无法创建/usr/bin 符号链接(symbolic link)

    c++ - C++ 应用程序的框架

    javascript - 如何使DIV无法聚焦?

    android - 用户交互能否在 OnResume 完成之前触发事件?