Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force a window to redraw itself using Core Graphics?

I have developed injection system and have hooked some quartz API's to create some nice effects with windows on Mac OS X. For example, when user sets a color to red in window.. it is red glossy red.

But, when I inject in application which is already running, I cannot give it desired effects as window is already painted. So, I am looking for something in quartz/core graphics which can allow me to redraw whole window or some technique which can allow me to send some event/call some function which will make system repaint whole window again.

I mean every thing on window is to be draw again so that my hooked API's will execute in order to create proper effects, shades and colors. Here order in which window gets created & painted is important.

I am using technique similar to inject&interpose and injection code is C/C++ code.

Does anyone have an idea how can I achieve this?

like image 520
RLT Avatar asked Oct 04 '11 12:10

RLT


2 Answers

-[NSView setNeedsDisplayInRect:] and -[NSView setNeedsDisplay:] are the direct equivalents of invalidateRect.

I don't know what you mean by that you need it in Quartz/CoreGraphics. Cocoa is already using them for drawing.

If you want to call some magic CGxxx() function that will make the window painted again, it can't be done. The window's title and frame are painted by the system, but as for the content, there is no way for lower level APIs to know what should be painted there. The only one who knows how to draw a view is the view itself. (Maybe there is something cached in window's backing store, but I don't know any public or undocumented APIs to access it).

Whatever you find just has to based on asking the NSWindow object to redraw its views. If you're already injected into a process, it may involve the following steps:

  • locating obj-c runtime (you will need at least objc_msgSend function)
  • locating NSApplication class
  • using +[NSApplication sharedApplication] and -[NSApplication windows] to find NSWindow* object pointer
  • using contentView, display etc. to redraw
like image 153
hamstergene Avatar answered Oct 08 '22 11:10

hamstergene


If you're asking for a way of forcing a window to redraw itself using a lower-level API than Cocoa, then as far as I know, it isn't possible. A window redraws itself when its content view's drawRect: method is called. It passes in a CGContextRef to the window, which the method then uses to redraw the window. CoreGraphics is not responsible for redrawing a window. Cocoa uses CoreGraphics to redraw windows.

It is possible to obtain a window's graphicscontext outside of drawRect: and then draw to that whenever you want (see, eg., here), but it sounds like what you really want to do is intercept the results of the window's ordinary drawing routine and do some of your own stuff on top. You could probably do this by switching the class of the window's content view and overriding drawRect. A helper function to handle the injection would look something like this:

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))
}

Edit:

Ahhh, you're interested in the whole window. The frame, etc. are also NSView instances, but they're private subclasses of NSView that you don't have direct access to. You could force them to redraw by calling display on the window, but this will likely overwrite whatever you've done to the window as it will use the existing drawing routines of these classes.

So you might also want to consider swizzling the drawRect: method of these views (the call to [[NSGraphicsContext currentContext] graphicsPort] in drawRect: will give you a CGContextRef that you can use with the Quartz APIs). You could get the frame views by calling superview on the window's content view.

Note that the arrangement of the window's frame views are undocumented and could change with system updates.

Sounds like an interesting project, anyway!

like image 5
Chris Devereux Avatar answered Oct 08 '22 10:10

Chris Devereux