Pergunta

Estou tentando encontrar as APIs documentadas (ou não documentadas, se essa for minha única opção) no OS X para consultar uma lista de Windows do servidor de janelas e, em seguida, fazer com que o Windows se mova e redimensione. Alguém pode me apontar na direção certa? Acho que eu começaria com algo como FindWindowex e MoveWindow sob Win32.

Observe que quero fazer isso em um processo externo - não estou perguntando como controlar apenas o tamanho e a posição da janela do meu aplicativo.

Foi útil?

Solução

Use a API de acessibilidade. Usando esta API, você pode se conectar a um processo, obter uma lista de Windows (na verdade, uma matriz), obtenha as posições e tamanhos de cada janela e também altere as propriedades da janela, se quiser.

No entanto, um aplicativo só pode estar usando essa API se o usuário tiver permitido o acesso a dispositivos de assistência em suas preferências (pré -fs do sistema -> acesso universal), caso em que todos os aplicativos podem usar essa API ou se seu aplicativo for um aplicativo de assistência confiável (Quando é confiável, pode usar a API, mesmo que essa opção não seja verificada). A própria API de acessibilidade oferece as funções necessárias para tornar seu aplicativo confiável - basicamente você deve se tornar root (usando serviços de segurança para solicitar permissões raiz do usuário) e depois marcar seu processo como confiável. Depois que seu aplicativo é marcado, ele deve ser reiniciado, pois o estado confiável é verificado apenas na inicialização e não pode mudar enquanto o aplicativo estiver em execução. O estado de confiança é permanente, a menos que o usuário mova o aplicativo em outro lugar ou o hash das alterações binárias do aplicativo (por exemplo, após uma atualização). Se o usuário tiver dispositivos de assistência ativados em seus prefs, todos os aplicativos serão tratados como se fossem confiáveis. Normalmente, seu aplicativo verificaria se essa opção estiver ativada, se for, continue e faça suas coisas. Caso contrário, verificaria se já é confiável, se for, novamente, faça suas coisas. Se não for, tente se tornar confiável e reinicie o aplicativo, a menos que o usuário recusasse a autorização raiz. A API oferece todas as funções necessárias para verificar tudo isso.

Existem funções privadas para fazer o mesmo usando o Mac OS Window Manager, mas a única vantagem que o compraria é que você não precisa ser um aplicativo de acessibilidade confiável (que é uma operação única no primeiro lançamento na maioria dos casos) . As desvantagens são que essa API pode mudar a qualquer momento (ela já mudou no passado), tudo não é documentado e as funções são conhecidas apenas através da engenharia reversa. A acessibilidade, no entanto, é pública, está documentada e não muda muito desde a primeira versão do OS X que a introduziu (algumas novas funções foram adicionadas em 10.4 e novamente em 10.5, mas não muito mais mudou).

Aqui está um exemplo de código. Ele aguardará 5 segundos, para que você possa mudar para uma janela diferente antes de fazer qualquer outra coisa (caso contrário, ele sempre funcionará com a janela do terminal, bastante chato para testes). Em seguida, ele obtém o processo mais frontal, a maioria das janelas da frente desse processo, imprimirá sua posição e tamanho e, finalmente, moverá -o em 25 pixels para a direita. Você o compila na linha de comando como essa (assumindo que é nomeado test.c)

gcc -framework Carbon -o test test.c

Observe que eu não realizo nenhum erro de verificação do código por simplicidade (existem vários lugares que podem fazer com que o programa falhe se algo der errado e certas coisas podem/podem dar errado). Aqui está o código:

/* 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 perguntou como você recebe uma lista de todas as janelas nos comentários, eis como:

Em vez de "kaxfocusedwindowattribute" você usa "kaxwindowsattribute" para a função AxuElementCopyAttributeValue. O resultado não é o AxuieLelementRef, mas um CFARRAY de elementos AxUieLEMELEmentRef, um para cada janela deste aplicativo.

Outras dicas

Concordo que a acessibilidade é o melhor caminho a seguir. Mas se você quiser, o AppleScript também funcionará.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top