Frage

Ich versuche, dokumentierte (oder, undokumentierte, wenn dies meine einzige Option ist) APIs auf OS X zu finden, um eine Liste von Fenstern aus dem Fensterserver abzufragen und dann die Fenster zu verschieben und zu ändern. Kann mir jemand in die richtige Richtung verweisen? Ich denke, ich würde mit so etwas wie FindWindowex und MoveWindow unter Win32 beginnen.

Beachten Sie, dass ich dies aus einem externen Prozess tun möchte - ich frage nicht, wie ich nur die Fenstergröße und Position meiner eigenen App steuern soll.

War es hilfreich?

Lösung

Verwenden Sie die API der Barrierefreiheit. Mit dieser API können Sie eine Verbindung zu einem Prozess herstellen, eine Liste von Fenstern (eigentlich ein Array) erhalten, die Positionen und Größen jedes Fensters erhalten und auch die Fenstereigenschaften ändern, wenn Sie möchten.

Eine Anwendung kann diese API jedoch nur verwenden, wenn der Benutzer in seinen Präferenzen Zugriff auf Hilfsmittel aktiviert hat (Wenn es vertraut ist, kann die API auch dann verwendet werden, auch wenn diese Option nicht überprüft wird). Die API der Barrierefreiheit selbst bietet die erforderlichen Funktionen, um Ihre Bewerbung vertrauen zu lassen. Grundsätzlich müssen Sie Root werden (verwenden Sicherheitsdienste, um Root -Berechtigungen des Benutzers anzufordern) und dann Ihren Prozess wie vertrauenswürdig zu markieren. Sobald Ihre Bewerbung vertrauenswürdig markiert wurde, muss sie neu gestartet werden, da der vertrauenswürdige Staat nur beim Start überprüft wird und sich während der Ausführung der App nicht ändern kann. Der Vertrauenszustand ist dauerhaft, es sei denn, der Benutzer bewegt die Anwendung an einen anderen Ort oder den Hash der Anwendung Binäränderungen (z. B. nach einem Update). Wenn der Benutzer in seinen PreFs assistiven Geräte aktiviert ist, werden alle Anwendungen so behandelt, als ob er vertrauenswürdig wäre. Normalerweise würde Ihre App prüfen, ob diese Option aktiviert ist, falls dies der Fall ist, und machen Sie Ihre Sachen. Wenn nicht, würde es überprüfen, ob es bereits vertrauenswürdig ist, wenn dies der Fall ist, einfach wieder Ihre Sachen machen. Wenn Sie nicht versuchen, sich selbst zu vertrauen, und dann die Anwendung neu starten, es sei denn, der Benutzer lehnte die Stammberechtigung ab. Die API bietet alle notwendigen Funktionen, um all dies zu überprüfen.

Es gibt private Funktionen, um mit dem Mac OS -Fenstermanager dasselbe zu erfüllen. Der einzige Vorteil, der Sie kaufen würde, besteht darin, dass Sie keine vertrauenswürdige Barrierefreiheitsanwendung sein müssen (was in den meisten Fällen ein einmaliger Betrieb beim ersten Start ist). . Die Nachteile sind, dass sich diese API jederzeit ändern kann (sie hat sich in der Vergangenheit bereits geändert), alles undokumentiert und Funktionen werden nur durch Reverse Engineering bekannt. Die Zugänglichkeit ist jedoch öffentlich, es ist dokumentiert und hat sich seit der ersten OS X -Version, die sie eingeführt hat, nicht viel geändert (einige neue Funktionen wurden in 10,4 und erneut in 10,5 hinzugefügt, aber sonst hat sich nicht viel geändert).

Hier ist ein Code -Beispiel. Es wird 5 Sekunden warten, sodass Sie zu einem anderen Fenster wechseln können, bevor es etwas anderes tut (ansonsten funktioniert es immer mit dem Terminalfenster, eher langweilig zum Testen). Dann wird es den meisten Vorgang, das vorderste Fenster dieses Vorgangs, drucken Sie die Position und Größe und verschieben ihn schließlich um 25 Pixel nach rechts. Sie kompilieren es so in der Befehlszeile (vorausgesetzt, es heißt test.c)

gcc -framework Carbon -o test test.c

Bitte beachten Sie, dass ich keine Fehlerprüfung im Code zur Einfachheit durchführe (es gibt verschiedene Orte, an denen das Programm abstürzt, wenn etwas schief geht und bestimmte Dinge schief gehen können). Hier ist der Code:

/* Carbon includes everything necessary for Accessibilty API */
#include <Carbon/Carbon.h>

static bool amIAuthorized ()
{
    if (AXAPIEnabled() != 0) {
        /* Yehaa, all apps are authorized */
        return true;
    }
    /* Bummer, it's not activated, maybe we are trusted */
    if (AXIsProcessTrusted() != 0) {
        /* Good news, we are already trusted */
        return true;
    }
    /* Crap, we are not trusted...
     * correct behavior would now be to become a root process using
     * authorization services and then call AXMakeProcessTrusted() to make
     * ourselves trusted, then restart... I'll skip this here for
     * simplicity.
     */
    return false;
}


static AXUIElementRef getFrontMostApp ()
{
    pid_t pid;
    ProcessSerialNumber psn;

    GetFrontProcess(&psn);
    GetProcessPID(&psn, &pid);
    return AXUIElementCreateApplication(pid);
}


int main (
    int argc,
    char ** argv
) {
    int i;
    AXValueRef temp;
    CGSize windowSize;
    CGPoint windowPosition;
    CFStringRef windowTitle;
    AXUIElementRef frontMostApp;
    AXUIElementRef frontMostWindow;

    if (!amIAuthorized()) {
        printf("Can't use accessibility API!\n");
        return 1;
    }

    /* Give the user 5 seconds to switch to another window, otherwise
     * only the terminal window will be used
     */
    for (i = 0; i < 5; i++) {
        sleep(1);
        printf("%d", i + 1);
        if (i < 4) {
            printf("...");
            fflush(stdout);
        } else {
            printf("\n");
        }
    }

    /* Here we go. Find out which process is front-most */
    frontMostApp = getFrontMostApp();

    /* Get the front most window. We could also get an array of all windows
     * of this process and ask each window if it is front most, but that is
     * quite inefficient if we only need the front most window.
     */
    AXUIElementCopyAttributeValue(
        frontMostApp, kAXFocusedWindowAttribute, (CFTypeRef *)&frontMostWindow
    );

    /* Get the title of the window */
    AXUIElementCopyAttributeValue(
        frontMostWindow, kAXTitleAttribute, (CFTypeRef *)&windowTitle
    );

    /* Get the window size and position */
    AXUIElementCopyAttributeValue(
        frontMostWindow, kAXSizeAttribute, (CFTypeRef *)&temp
    );
    AXValueGetValue(temp, kAXValueCGSizeType, &windowSize);
    CFRelease(temp);

    AXUIElementCopyAttributeValue(
        frontMostWindow, kAXPositionAttribute, (CFTypeRef *)&temp
    );
    AXValueGetValue(temp, kAXValueCGPointType, &windowPosition);
    CFRelease(temp);

    /* Print everything */
    printf("\n");
    CFShow(windowTitle);
    printf(
        "Window is at (%f, %f) and has dimension of (%f, %f)\n",
        windowPosition.x,
        windowPosition.y,
        windowSize.width,
        windowSize.height
    );

    /* Move the window to the right by 25 pixels */
    windowPosition.x += 25;
    temp = AXValueCreate(kAXValueCGPointType, &windowPosition);
    AXUIElementSetAttributeValue(frontMostWindow, kAXPositionAttribute, temp);
    CFRelease(temp);

    /* Clean up */
    CFRelease(frontMostWindow);
    CFRelease(frontMostApp);
    return 0;
}

Sine Ben fragte, wie Sie in den Kommentaren eine Liste aller Fenster erhalten. Hier erfahren Sie, wie:

Anstelle von "kaxfocusedWindowattribute" verwenden Sie "kaxwindowsattribute" für die AxuielementCopyAttributeValue -Funktion. Das Ergebnis ist dann kein AxuielementRef, sondern ein Cfarray von AxuielementRef -Elementen, eines für jedes Fenster dieser Anwendung.

Andere Tipps

Ich bin damit einverstanden, dass Barrierefreiheit der beste Weg nach vorne ist. Wenn Sie jedoch schnell und schädlich möchten, funktioniert Apfelkript ebenfalls.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top