Перемещение других окон в Mac OS X с помощью API специальных возможностей

Я пытаюсь использовать API специальных возможностей для изменения положения окон других приложений. Я хочу получить все окна на экране из всех приложений, а затем переместить их все с заданным смещением (скажем, 5 или 10 или любое значение). У меня возникают трудности с этим, поскольку для меня это первый день программирования на Objective-C.

Вот что я сейчас делаю. Сначала я нахожу список окон и их PID, используя CGWindowListCopyWindowInfo. Затем для каждого окна я использую AXUIElementCreateApplication, чтобы получить AXUIElementRef окна. После этого я должен использовать AXUIElementCopyAttributeValue с атрибутом kAXPositionAttribute (который мне не удается получить правильную позицию, всегда получаются нули). Наконец, я должен добавить желаемое смещение к позиции и использовать AXUIElementSetAttributeValue с атрибутом kAXPositionAttribute и новой точкой положения (что я получаю ошибки времени выполнения, даже если я установил абсолютные значения, такие как 0,0).

Может ли кто-нибудь помочь мне с фрагментом, делающим то, что я описал выше, поскольку я пробовал много вещей безуспешно. Кроме того, это не должно быть в точности таким, как я решил реализовать выше. Если есть лучший способ сделать это, я буду рад его изменить.

Обновление: как указано в комментарии, вот фрагмент кода одной из попыток:

// Get all the windows
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray* arr = CFBridgingRelease(windowList);
// Loop through the windows
for (NSMutableDictionary* entry in arr)
{
    // Get window PID
    pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
    // Get AXUIElement using PID
    AXUIElementRef elementRef = AXUIElementCreateApplication(pid);
    CFTypeRef position;
    CGPoint point;
    // Get the position attribute of the window (maybe something is wrong?)
    AXUIElementCopyAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
    AXValueGetValue(position, kAXValueCGPointType, &point);
    // Debugging (always zeros?)
    NSLog(@"point=%@", point);
    // Create a point
    NSPoint newPoint;
    newPoint.x = 0;
    newPoint.y = 0;
    position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
    // Set the position attribute of the window (runtime error over here)
    AXUIElementSetAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
}

person tria    schedule 11.01.2014    source источник
comment
Опубликуйте код одной из своих попыток, чтобы люди увидели, с чего вы начали.   -  person gaige    schedule 12.01.2014
comment
Это было объяснено по шагам, и то, что у меня было, не работает, поэтому я подумал, что нет необходимости в неработающем. Я обновил его фрагментом.   -  person tria    schedule 12.01.2014


Ответы (1)


Основываясь на вашем примере кода (слегка измененном, поскольку то, что вы опубликовали, не компилируется и вылетает без изменений), я провел несколько экспериментов.

Вот несколько предостережений:

  • Вы получаете Приложение по PID, но затем действуете в нем, как если бы это было окно. В этом суть вашей проблемы, но это только начало решения.
  • Вам нужно будет просмотреть список окон для объекта приложения специальных возможностей, чтобы найти перемещаемые окна, которые можно перемещать с помощью Accessibility Framework.
  • CGWindowListCopyWindowInfo вернет окна "все на экране", когда вас спросят, как вы его вызываете, но это не гарантирует, что это либо "пользовательские окна", либо окна со специальными возможностями. У большинства элементов строки меню есть корневое окно, которое находится «на экране», и большинство из них недоступны (что появляется, когда вы пытаетесь пройти по дереву доступности для извлеченных PID).
  • Вы можете найти тест для AXRole как полезный, или вы можете найти другие атрибуты доступности окон, более полезные при определении того, перемещать ли окна или нет.

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

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>

int main(int argc, char *argv[]) {
    @autoreleasepool {
    // Get all the windows
    CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
    NSArray* arr = CFBridgingRelease(windowList);
    // Loop through the windows
    for (NSMutableDictionary* entry in arr)
    {
        // Get window PID
        pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
        // Get AXUIElement using PID
        AXUIElementRef appRef = AXUIElementCreateApplication(pid);
        NSLog(@"Ref = %@",appRef);

        // Get the windows
        CFArrayRef windowList;
        AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);
        NSLog(@"WindowList = %@", windowList);
        if ((!windowList) || CFArrayGetCount(windowList)<1)
            continue;


        // get just the first window for now
        AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, 0);
        CFTypeRef role;
        AXUIElementCopyAttributeValue(windowRef, kAXRoleAttribute, (CFTypeRef *)&role);         
        CFTypeRef position;
        CGPoint point;

        // Get the position attribute of the window (maybe something is wrong?)
        AXUIElementCopyAttributeValue(windowRef, kAXPositionAttribute, (CFTypeRef *)&position);
        AXValueGetValue(position, kAXValueCGPointType, &point);
        // Debugging (always zeros?)
        NSLog(@"point=%f,%f", point.x,point.y);
        // Create a point
        CGPoint newPoint;
        newPoint.x = 0;
        newPoint.y = 0;
        NSLog(@"Create");
        position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
        // Set the position attribute of the window (runtime error over here)
        NSLog(@"SetAttribute");
        AXUIElementSetAttributeValue(windowRef, kAXPositionAttribute, position);
        sleep(5);
    }       
    }
}
person gaige    schedule 12.01.2014
comment
Отличный ответ, спасибо! Проголосовали и выбрали. Однако не могли бы вы подробнее рассказать об использовании AXRole, как он будет полезен в этом случае? - person tria; 13.01.2014
comment
@tria AXRole (в зависимости от его использования приложениями, окна которых доступны) может помочь вам решить, какие окна нужно переместить, например kAXSystemWideRole, kAXSheetRole. Вы можете осмотреть свою собственную систему с помощью Accessibility Inspector (в наши дни он установлен как часть Xcode). - person gaige; 13.01.2014
comment
@gaige кажется, что AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList); в macOS Sierra не вернет ни одного окна. - person loretoparisi; 19.10.2016
comment
@loretoparisi Я вижу, что это тоже повторный запуск моего тестового приспособления. Я предполагаю, что это связано с моделью разрешений, поскольку я не пробовал это в полном приложении, где я могу предоставить разрешения доступа. - person gaige; 20.10.2016
comment
@gaige Есть идеи, как это сделать в Swift? Я не могу заставить его работать со всеми этими указателями - person mica; 25.11.2017
comment
@mica Я еще не очень много играл со Swift и AX. Вы можете попробовать разделить вышеперечисленное на методы, а затем использовать способность Xcode использовать Swift и Objective-C бок о бок для доступа к методам. - person gaige; 26.11.2017
comment
@gaige Tnx, пытается что-то подобное здесь stackoverflow.com/questions/47480873/ - person mica; 26.11.2017