2011-10-04 16 views
6

He desarrollado un sistema de inyección y he conectado algunas API de cuarzo para crear efectos agradables con Windows en Mac OS X. Por ejemplo, cuando el usuario establece un color en rojo en la ventana ... es de color rojo brillante.¿Obliga a una ventana a redibujarse utilizando Core Graphics?

Pero, cuando lo inyecto en una aplicación que ya se está ejecutando, no puedo darle los efectos deseados ya que la ventana ya está pintada. Por lo tanto, estoy buscando algo en gráficos de cuarzo/núcleo que me permita redibujar toda la ventana o alguna técnica que me permita enviar algún evento/invocar alguna función que hará que el sistema repinte toda la ventana nuevamente.

Quiero decir que cada cosa en la ventana es dibujar nuevamente para que mis API enganchadas se ejecuten con el fin de crear efectos, tonos y colores adecuados. Aquí el orden en que se crea la ventana & es importante.

Estoy usando una técnica similar a inject&interpose y el código de inyección es el código C/C++.

¿Alguien tiene una idea de cómo puedo lograr esto?

+2

¿Hay algo así como invalidateRect en Mac que obligue a redibujar las ventanas? – MacGeek

Respuesta

5

-[NSView setNeedsDisplayInRect:] y -[NSView setNeedsDisplay:] son los equivalentes directos de invalidateRect.

No sé a qué te refieres con que lo necesitas en Quartz/CoreGraphics. Cocoa ya los está usando para dibujar.

Si desea llamar a alguna función mágica CGxxx() que volverá a pintar la ventana, no se puede hacer. El título y el marco de la ventana están pintados por el sistema, pero en cuanto al contenido, no hay forma de que las API de nivel inferior sepan qué se debe pintar allí. El único que sabe cómo dibujar una vista es la vista en sí misma. (Tal vez hay algo guardado en la memoria de la ventana, pero no conozco ninguna API pública o no documentada para acceder a ella).

Lo que encuentre solo tiene en función de pedirle al objeto NSWindow que vuelva a dibujar sus vistas. Si ya está inyectado en un proceso, que puede implicar los siguientes pasos:

  • localizar obj-c tiempo de ejecución (necesitará al menos objc_msgSend función)
  • localizar clase NSApplication
  • usando +[NSApplication sharedApplication] y -[NSApplication windows] para encontrar NSWindow* puntero de objeto
  • usando contentView, etc. display redibuja la pantalla
+0

Lo siento, debería haber agregado más detalles. Quiero solución en cuarzo. – MacGeek

+0

@MachinTosh No estoy seguro de que te entendamos, pero mira mi respuesta actualizada. – hamstergene

+0

Su respuesta ofrece un buen enfoque para la solución. Estaba buscando algo de magia CGxxx (: D) porque estoy tratando de estar en el nivel de Cuarzo y estaba tratando de evitar el swizzling. – MacGeek

5

Si está pidiendo una forma de forzar una ventana para volver a dibujarse usando una API de nivel inferior a la de Cocoa, entonces, que yo sepa, no es posible. Una ventana se redibuja cuando se llama al método drawRect: de su vista de contenido. Pasa un CGContextRef a la ventana, que luego el método utiliza para volver a dibujar la ventana. CoreGraphics no es responsable de volver a dibujar una ventana. Cocoa usa CoreGraphics para redibujar ventanas.

Es posible obtener graphicscontext de una ventana exterior de drawRect: (. Véase, por ejemplo, here) y luego dibujar a que siempre que lo desee, pero suena como lo que realmente desea hacer es interceptar los resultados de la rutina de dibujo ordinaria de la ventana y haz algunas de tus cosas en la parte superior. Probablemente puedas hacer esto cambiando la clase de la vista de contenido de la ventana y reemplazando drawRect.Una función auxiliar para manejar la inyección sería algo como esto:

typedef void (^InjectedBlock)(CGContextRef, CGRect); 

void InjectIntoView(NSView* view, InjectedBlock aBlock) 
{ 
    Class viewClass = [view class]; 
    InjectedBlock injectedBlock = [aBlock copy]; 

    void(^drawRect)(id, SEL, NSRect) = ^(id self, SEL _cmd, NSRect rect) 
    { 
     struct objc_super superId = { self, viewClass }; 
     objc_msgSendSuper(superId, @selector(drawRect:), rect); 

     injectedBlock([[NSGraphicsContext currentContext] graphicsPort], CGRectFromNSRect(rect)); 
    }; 

    NSString* subclassName = [NSString stringWithFormat:"%s_injected", class_getName(viewClass)] 
    Class subclass objc_allocateClassPair(viewClass, [subclassName UTF8String], 0); 
    objc_registerClassPair(subclass); 

    Method overriddenMethod = class_getInstanceMethod([NSView class], @selector(drawRect:)); 
    IMP imp = imp_implementationWithBlock(drawRect); 

    class_addMethod(subclass, @selector(drawRect:), imp, method_getTypeEncoding(overriddenMethod)) 
} 

Editar:

Ahhh, está interesado en toda la ventana. El marco, etc. también son instancias NSView, pero son subclases privadas de NSView a las que no tiene acceso directo. Puede obligarlos a volver a dibujar llamando al display en la ventana, pero es probable que esto sobrescriba lo que haya hecho en la ventana, ya que utilizará las rutinas de dibujo existentes de estas clases.

Así que también es posible que desee plantear el método drawRect: estas vistas (la llamada a [[NSGraphicsContext currentContext] graphicsPort] en drawRect: le dará un CGContextRef que puede usar con las API de Quartz). Puede obtener las vistas de marco llamando al superview en la vista de contenido de la ventana.

Tenga en cuenta que la disposición de las vistas de marco de la ventana no está documentada y podría cambiar con las actualizaciones del sistema.

¡Suena como un proyecto interesante, de todos modos!

+0

obtengo la primera parte de su respuesta. Pero no el segundo. Por lo tanto, CoreGraphics no dibujará en el cacao window does. ¡Lo suficientemente justo! Pero en la segunda parte mostró swizzling (no conocía esta técnica, gracias). Sin embargo, una solución más adecuada podría ser de alguna forma cómo enviar drawRect:] instrucción a la ventana? Estoy usando una técnica similar a 'https://github.com/comex/inject_and_interpose/'. Entonces su código C/C++. ¿Puedes sugerir algo relacionado con esto? – MacGeek

+0

Creo que podría haber entendido mal lo que estás haciendo ... ¿Tal vez podrías decir algo más sobre lo que haces en las ventanas de la aplicación en la que lo inyectas? –

+0

He actualizado la pregunta. – MacGeek

3

No he encontrado algo que invalide una rect, pero como su pregunta es cómo volver a dibujar una ventana completa, no parece ser lo que necesita de todos modos.

Cuando se invalida, le dice al sistema que una parte de su vista no es válida. La próxima vez que su sistema tenga tiempo disponible para dibujar (generalmente al instante después de declarar la invalidación), volverá a dibujar el rect invalidado.

setNeedsDisplay hace exactamente lo mismo, excepto para una vista completa y no un rect específico dentro de esa vista. En su pregunta, esto no importa, ya que desea actualizar toda la ventana. Se encadena hacia abajo desde UIView a Quartz al sistema interno, por lo que también se utilizará su dibujo de Quartz, siempre que lo maneje a través de drawRect, que se llama al dibujar.

Simplemente llame a [suWindow setNeedsDisplay]; y el sistema sabrá que su ventana necesita ser rediseñada lo más pronto posible.

Cuestiones relacionadas