2011-09-14 13 views
26

Estoy intentando parchar an application que redimensiona las ventanas usando la API de accesibilidad.Identificar de forma exclusiva la ventana activa en OS X

Necesito mantener un diccionario con los tamaños anteriores de windows. La clave necesita identificar la ventana actualmente activa. Por el momento, esta ventana activa se recupera a través del NSAccessibilityFocusedWindowAttribute al presionar una tecla de acceso directo.

Sin embargo, cada vez que se llama a este método, el AXUIElementRef devuelto que identifica la ventana es diferente! Esto, por supuesto, significa que no puedo usarlo como una clave de diccionario; el diccionario no encontrará la entrada correspondiente.

El código siguiente reproduce el problema:

-(IBAction)testWindowIdentification:(id)sender{ 
    AXUIElementRef focusedApp; 
    AXUIElementRef focusedWindow; 

    AXUIElementCopyAttributeValue(_systemWideElement, 
            (CFStringRef) kAXFocusedApplicationAttribute, 
            (CFTypeRef*) &focusedApp); 
    AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp, 
            (CFStringRef) NSAccessibilityFocusedWindowAttribute, 
            (CFTypeRef*) &focusedWindow); 
    CFShow(focusedWindow); 
} 

_systemWideElement se ha inicializado en el método init mediante una llamada a AXUIElementCreateSystemWide().

La declaración CFShow muestra claramente diferentes ID cada vez que se llama al método (a pesar de que la misma ventana está activa), que es inútil para mí:

<AXUIElement 0x47e850> {pid=42463} 
<AXUIElement 0x47e890> {pid=42463} 
<AXUIElement 0x47e2c0> {pid=42463} 
… 

El documentation on AXUIElement muestra ningún método que recupera un atributo único para el elemento UI, y tampoco lo hace that of the NSAccessibility protocol. El PID único es no suficiente para mí, ya que un proceso puede tener varias ventanas.

¿Cómo puedo recuperar algún identificador único de la ventana activa en Cocoa?

(Por cierto, el código real es el control de los códigos de retorno en las llamadas anteriores; no hay ningún error, las llamadas a tener éxito.)

+1

@JeremyBanks El que responde originalmente tiene la idea correcta aquí. De hecho, puede usar Quartz para obtener un 'CGWindowID' una vez que haya determinado la ventana enfocada, si [esta respuesta] (http://stackoverflow.com/a/312099/517815) es de creer. Este _debería darle el identificador de ventana único que está esperando, que puede pasar impunemente en el contexto de su aplicación actual. Avíseme si desea una versión más coherente y completa de esto como una respuesta real. – MrGomez

+0

@MrGomez Seguro, una respuesta como esa sería genial. :) –

+0

@JeremyBanks Will do. Estoy un poco sobrecargado hoy, pero trataré de obtener una respuesta a esto en algún momento más tarde esta noche (PST). :) – MrGomez

Respuesta

16

Rob Keniger tiene la estrategia correcta con his answer here. Lo único que falta de esta respuesta (y de hecho, el motivo de la colocación de recompensas) es una implementación viable que toma la ventana activa actual y la traduce en una clave única adecuada para la indexación en el contexto de la aplicación actual en funcionamiento.

La solución de Rob esboza esto mediante el uso del CGWindowID dado en el contexto de Quartz Window Services. Está, por supuesto, fuertemente implícito que esta ventana de referencia es only useful for your current application.

Obtener esta ventana de referencia es complicado, porque no existen garantías sólidas entre la API de accesibilidad y los servicios de ventana de cuarzo. Sin embargo, puede solucionar este problema en las siguientes maneras:

  1. Uso extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);, como documented here.Esto no es garantizado para funcionar, pero funciona como una prueba de planta baja para que las cosas funcionen si funciona en su versión de OSX.

  2. Obtenga el CGWindowID directamente, usando, por ejemplo, HIWindowGetCGWindowID(). Se pueden encontrar más detalles sobre cómo seleccionar la ventana activa y extraer la ID en the reference manual for the Carbon Window Manager (advertencia: PDF grande).

  3. Catalogue su conjunto CGWindowID usando algo como CGWindowListCreateDescriptionFromArray, exactamente como Rob sugirió. El objetivo aquí es encontrar un esquema para enlazar la API de Accesibilidad y Quartz, pero esto es concebible utilizando, por ejemplo, una devolución de llamada vinculada al contexto de su ventana activa actual. Honestamente, no conozco un ejemplo óptimo de este que esté debidamente preparado para el futuro, sin embargo.

De las opciones, recomiendo ir con 2. para sus necesidades actuales, si no es capaz de crear otro decorador de ventanas para identificar de forma exclusiva ellos. Actualmente está definido en la base del código heredado, pero hará lo que usted desee.

La mejor de las suertes con su aplicación.

+0

Cómo detectar que no se ha seleccionado ninguna ventana o que todas las ventanas están inactivas (es decir, el control está en el escritorio)? –

+0

Disculpe, cómo usar "extern" C "AXError _AXUIElementGetWindow (AXUIElementRef, CGWindowID * out);" en Swift? – allenlinli

8

Creo que podría ser capaz de utilizar las funciones de cuarzo ventana Servicios , específicamente CGWindowListCreateDescriptionFromArray para enumerar las ventanas actualmente activas en una aplicación en particular.

Esta llamada es de nivel inferior a AppKit y no le indicará cuál es la ventana activa, pero le dará identificadores de ventana que son únicos para la sesión de usuario actual. No es una gran solución, pero podría comparar la información de límites de ventana con lo que recibe de las API de accesibilidad para asociar ventanas con sus ID reales.

Cuestiones relacionadas