如果应用程序关注macOS,如何获取? [英] How to get if an application has focus on macOS?
问题描述
我需要收集哪个应用程序具有焦点.为此,我的方法是:列出窗口,重点关注一个窗口,最后检查哪个进程和应用程序显示了它.如果有的话:getWindowWithFocus(),那就太好了.
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.
要求:
- 该程序是用C ++实现的,但可以在需要时与Objective-C交互.
- 该程序将以root特权运行.
- 列出的窗口列表必须包含所有个用户应用程序.
- 返回的窗口允许获取属性,例如处理过程以及是否具有UI焦点.
- 理想情况下,不使用任何第三方工具,而仅使用标准库(STL,Unix API和macOS API,最终是Qt/Boost).
- 必须支持BigSur的HSierra.
我设法列出了所有窗口,但现在我正努力检测窗口是否具有焦点.
I managed to list all windows, but now I am struggling in detecting if a window has or not the focus.
- 哪个API函数可用于检查窗口是否具有焦点?有样品吗?
- 有更好的方法解决这个问题吗?
我创建了一个POC/示例,其中列出了所有窗口,包括其中的一些属性.
I created a POC/sample which list all windows, including some of it properties.
CGWindowListCopyWindowInfo
免责声明:这是一个POC,仅用于演示,错过了适当项目所需的代码质量.例如,CFObject不会随内存泄漏而释放.
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?
}
}
推荐答案
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
focus_oc_wrapper.hpp
namespace FocusDetector
{
struct AppFocusImpl;
struct AppFocus
{
AppFocusImpl* impl=nullptr;
AppFocus() noexcept;
~AppFocus();
void run();
};
}
focus_oc_wrapper.mm
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
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
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} )
这篇关于如果应用程序关注macOS,如何获取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!