Question

J'essaie de trouver des API documentées (ou non documentées, si c'est ma seule option) sur OS X pour interroger une liste de fenêtres à partir du serveur de fenêtres, puis les déplacer et les redimensionner. Est-ce que quelqu'un peut-il me montrer la bonne direction? Je suppose que je commencerais par quelque chose comme FindWindowEx et MoveWindow sous Win32.

Notez que je souhaite procéder depuis un processus externe. Je ne vous demande pas comment contrôler uniquement la taille et la position de la fenêtre de ma propre application.

Était-ce utile?

La solution

Utilisez l'API d'accessibilité. En utilisant cette API, vous pouvez vous connecter à un processus, obtenir une liste de fenêtres (en réalité un tableau), obtenir la position et la taille de chaque fenêtre et modifier les propriétés de la fenêtre si vous le souhaitez.

Toutefois, une application ne peut utiliser cette API que si l'utilisateur a activé l'accès à ses périphériques d'assistance dans ses préférences (Préférences système - > Accès universel), auquel cas toutes les applications peuvent utiliser cette API ou si votre application est utilisée. une application associée de confiance (lorsqu'elle est approuvée, elle peut utiliser l'API, même si cette option n'est pas cochée). L'API d'accessibilité elle-même offre les fonctions nécessaires pour sécuriser votre application - vous devez en principe devenir root (en utilisant des services de sécurité pour demander les autorisations root de l'utilisateur), puis marquer votre processus comme approuvé. Une fois que votre application a été marquée comme étant approuvée, elle doit être redémarrée, car l’état approuvé n’est vérifié qu’au démarrage et ne peut pas être modifié tant que l’application est en cours d’exécution. L'état de confiance est permanent, sauf si l'utilisateur déplace l'application ailleurs ou que le hachage des modifications binaires de l'application (par exemple, après une mise à jour). Si des dispositifs d'assistance sont activés dans les préférences de l'utilisateur, toutes les applications sont traitées comme si elles étaient approuvées. Habituellement, votre application vérifie si cette option est activée, si c'est le cas, continuez et faites votre travail. Sinon, il vérifiera si c'est déjà fait, si c'est le cas, encore une fois, faites votre travail. Si ce n'est pas le cas, essayez de vous mettre en confiance, puis redémarrez l'application à moins que l'utilisateur ne refuse l'autorisation root. L’API offre toutes les fonctions nécessaires pour vérifier tout cela.

Il existe des fonctions privées permettant de faire la même chose à l'aide du gestionnaire de fenêtres Mac OS, mais le seul avantage qui vous en achèterait est que vous n'avez pas besoin d'être une application d'accessibilité fiable (opération unique à effectuer au premier lancement). la plupart des cas). L'inconvénient est que cette API peut changer à tout moment (elle a déjà changé par le passé), elle est tout non documentée et les fonctions ne sont connues que par le biais du reverse engineering. L’accessibilité est cependant publique, elle est documentée et elle n’a pas beaucoup changé depuis la première version d’OS X qui l’a introduite (de nouvelles fonctions ont été ajoutées dans les versions 10.4 et 10.5, mais peu d’autres ont changé).

Voici un exemple de code. Il attendra 5 secondes, vous pourrez donc basculer vers une fenêtre différente avant de faire quoi que ce soit (sinon, cela fonctionnera toujours avec la fenêtre du terminal, ce qui est plutôt ennuyeux pour les tests). Ensuite, il obtiendra le processus le plus en avant, la fenêtre la plus en avant de ce processus, imprimera sa position et sa taille et finalement le déplacera de 25 pixels vers la droite. Vous le compilez en ligne de commande comme ça (en supposant qu'il s'appelle test.c)

gcc -framework Carbon -o test test.c

Veuillez noter que je n’effectue aucune vérification d’erreur dans le code pour des raisons de simplicité (il existe différents endroits susceptibles de provoquer le blocage du programme en cas de problème ou de défaillance de certains éléments). Voici le 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 a demandé comment vous obtenez une liste de toutes les fenêtres dans les commentaires. Voici comment:

Au lieu de "kAXFocusedWindowAttribute" vous utilisez " kAXWindowsAttribute " pour la fonction AXUIElementCopyAttributeValue. Le résultat n'est alors pas AXUIElementRef, mais un CFArray d'éléments AXUIElementRef, un pour chaque fenêtre de cette application.

Autres conseils

Je conviens que l’accessibilité est la meilleure voie à suivre. Mais si vous voulez être rapide, AppleScript fonctionnera également.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top