2009-03-05 9 views
23

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

Notez que je veux faire cela à partir d'un processus externe - je ne demande pas comment contrôler la taille et la position de la fenêtre de ma propre application.

Répondre

42

Utilisez l'API Accessibilité. En utilisant cette API, vous pouvez vous connecter à un processus, obtenir une liste de fenêtres (en fait un tableau), obtenir les positions et les tailles de chaque fenêtre et également modifier les propriétés de la fenêtre si vous le souhaitez. Cependant, une application ne peut utiliser cette API que si l'utilisateur a activé l'accès pour les appareils fonctionnels 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 une application assitable de confiance (lorsqu'elle est approuvée, elle peut utiliser l'API, même si cette option n'est pas cochée). L'API Accessibility offre les fonctions nécessaires pour sécuriser votre application. En gros, vous devez devenir root (en utilisant les services de sécurité pour demander les autorisations root de l'utilisateur), puis marquer votre processus comme étant de confiance. Une fois que votre application a été marquée approuvée, elle doit être redémarrée car l'état sécurisé est uniquement vérifié au démarrage et ne peut pas être modifié pendant l'exécution de l'application. L'état de confiance est permanent, sauf si l'utilisateur déplace l'application ailleurs ou si le hachage du fichier binaire de l'application change (par exemple après une mise à jour). Si l'utilisateur a activé des périphériques fonctionnels dans ses préférences, 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 vos affaires. Si ce n'est pas le cas, il vérifie s'il est déjà approuvé, si c'est le cas, faites-le à nouveau. Si ce n'est pas le cas, essayez de vous faire confiance, puis redémarrez l'application sauf si l'utilisateur a refusé l'autorisation root. L'API offre toutes les fonctions nécessaires pour vérifier tout cela. Il existe des fonctions privées pour faire de même avec le gestionnaire de fenêtres Mac OS, mais le seul avantage qui vous achèterait, c'est que vous n'avez pas besoin d'être une application d'accessibilité fiable (opération unique au premier lancement) dans la plupart des cas). Les inconvénients sont que cette API peut changer à tout moment (elle a déjà changé dans le passé), tout est non documenté et les fonctions ne sont connues que par 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 en 10.4 et encore en 10.5, mais rien d'autre n'a changé).

Voici un exemple de code. Il attendra 5 secondes, donc vous pouvez passer à une fenêtre différente avant de faire quoi que ce soit d'autre (sinon cela fonctionnera toujours avec la fenêtre du terminal, 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, imprimer sa position et sa taille et enfin le déplacer de 25 pixels vers la droite. Vous compilez sur la ligne de commande comme celle (en supposant qu'il est nommé test.c)

gcc -framework Carbon -o test test.c 

S'il vous plaît noter que je ne fais pas d'erreur de vérification dans le code de la simplicité (il y a plusieurs endroits qui pourraient faire en sorte que le programme planter si quelque chose ne va pas et que certaines choses peuvent/peuvent aller mal). 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 est alors non AXUIElementRef, mais un CFArray d'éléments AXUIElementRef, un pour chaque fenêtre de cette application.

+0

"Nous pourrions également obtenir un tableau de toutes les fenêtres de ce processus" ... Comment ferais-je cela? –

+1

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

+0

Merci, juste ce que je cherchais. –

0

Je reconnais que l'accessibilité est la meilleure solution. Mais si vous voulez rapide et sale, AppleScript fonctionnera aussi bien.

Questions connexes