Как мое приложение может обнаружить изменение в окне другого приложения? - PullRequest
13 голосов
/ 12 мая 2009

В Какао на Mac я хотел бы определить, когда окно, принадлежащее другому приложению, перемещается, изменяется или перерисовывается. Как я могу это сделать?

Ответы [ 2 ]

43 голосов
/ 12 мая 2009

Вам потребуется использовать API-интерфейсы Accessibility, которые имеют обычный C и находятся внутри инфраструктуры ApplicationServices. Например:

Сначала вы создаете объект приложения:

AXUIElementRef app = AXUIElementCreateApplication( targetApplicationProcessID );

Тогда вы получите окно от этого. Вы можете запросить список окон и перечислить, или вы можете получить самое переднее окно (посмотрите в AXAttributeConstants.h все имена атрибутов, которые вы будете использовать).

AXUIElementRef frontWindow = NULL;
AXError err = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, &frontWindow );
if ( err != kAXErrorSuccess )
    // it failed -- maybe no main window (yet)

Теперь вы можете запросить уведомление через функцию обратного вызова C, когда свойство этого окна изменяется. Это четырехэтапный процесс:

Сначала вам понадобится функция обратного вызова для получения уведомлений:

void MyAXObserverCallback( AXObserverRef observer, AXUIElementRef element,
                           CFStringRef notificationName, void * contextData )
{
    // handle the notification appropriately
    // when using ObjC, your contextData might be an object, therefore you can do:
    SomeObject * obj = (SomeObject *) contextData;
    // now do something with obj
}

Далее вам нужен AXObserverRef, который управляет процедурой обратного вызова. Для этого требуется тот же идентификатор процесса, который вы использовали для создания элемента app выше:

AXObserverRef observer = NULL;
AXError err = AXObserverCreate( applicationProcessID, MyObserverCallback, &observer );
if ( err != kAXErrorSuccess )
    // handle the error

Получив своего наблюдателя, следующий шаг - запросить уведомление о некоторых вещах. Смотрите AXNotificationConstants.h для полного списка, но для изменения окна вам, вероятно, понадобятся только эти два:

AXObserverAddNotification( observer, frontWindow, kAXMovedNotification, self );
AXObserverAddNotification( observer, frontWindow, kAXResizedNotification, self );

Обратите внимание, что последний параметр, который передает предполагаемый объект "self" в качестве contextData. Это не сохраняется, поэтому важно вызвать AXObserverRemoveNotification, когда этот объект исчезнет.

Получив своего наблюдателя и добавив запросы на уведомления, вы теперь хотите прикрепить наблюдателя к своему циклу выполнения, чтобы вы могли отправлять эти уведомления асинхронным образом (или даже вообще):

CFRunLoopAddSource( [[NSRunLoop currentRunLoop] getCFRunLoop],
                    AXObserverGetRunLoopSource(observer),
                    kCFRunLoopDefaultMode );

AXUIElementRef s являются объектами в стиле CoreFoundation, поэтому вам нужно использовать CFRelease(), чтобы утилизировать их аккуратно. Например, для обеспечения чистоты вы должны использовать CFRelease(app) после получения элемента frontWindow, так как вам больше не нужно приложение.

Примечание о сборке мусора: чтобы сохранить AXUIElementRef в качестве переменной-члена, объявите его так:

__strong AXUIElementRef frontWindow;

Это указывает сборщику мусора отслеживать эту ссылку на него. При назначении для совместимости с GC и не-GC используйте это:

frontWindow = (AXUIElementRef) CFMakeCollectable( CFRetain(theElement) );
3 голосов
/ 12 мая 2009

Появились дальнейшие исследования "Кварц Дисплей Сервис"

Интересная функция для моих нужд - CGRegisterScreenRefreshCallback.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...