强制窗口使用Core Graphics重绘自己?

时间:2011-10-04 12:42:13

标签: objective-c macos cocoa core-graphics quartz-graphics

我已经开发了注射系统,并且已经连接了一些石英API,以便在Mac OS X上为窗口创建一些很好的效果。例如,当用户在窗口中将颜色设置为红色时,它是红色光泽的红色。

但是,当我注入已经运行的应用程序时,由于窗口已经被绘制,我无法提供所需的效果。所以,我正在寻找石英/核心图形的东西,它可以让我重绘整个窗口或一些技术,这可以让我发送一些事件/调用一些功能,这将使系统再次重新整个窗口。

我的意思是窗口上的每个东西都要再次绘制,这样我的钩子API就会执行,以便创建正确的效果,阴影和颜色。这里命令创建了哪个窗口&画很重要。

我使用的技术类似于inject&interpose,注入代码是C / C ++代码。

有谁知道如何实现这个目标?

3 个答案:

答案 0 :(得分:5)

-[NSView setNeedsDisplayInRect:]-[NSView setNeedsDisplay:]invalidateRect的直接等价物。

我不知道你在Quartz / CoreGraphics中需要它是什么意思。可可已经在使用它们进行绘画了。

如果你想调用一些魔术CGxxx()函数来重新绘制窗口,则无法完成。窗口的标题和框架由系统绘制,但对于内容,低级API无法知道应该在那里绘制什么。唯一知道如何绘制视图的人就是视图本身。 (也许在窗口的后备存储中有一些缓存,但我不知道任何公共或未记录的API来访问它。)

根据要求NSWindow对象重绘其视图,无论您发现。如果您已经注入流程,则可能涉及以下步骤:

  • 定位obj-c运行时(至少需要objc_msgSend函数)
  • 查找NSApplication类
  • 使用+[NSApplication sharedApplication]-[NSApplication windows]查找NSWindow*对象指针
  • 使用contentViewdisplay等重绘

答案 1 :(得分:5)

如果你想要一种强制窗口使用比Cocoa更低级别的API重绘自己的方法,那么据我所知,这是不可能的。当调用其内容视图的drawRect:方法时,窗口会重绘自身。它将CGContextRef传递给窗口,然后该方法用于重绘窗口。 CoreGraphics不负责重绘窗口。 Cocoa使用CoreGraphics重绘窗口。

可以在drawRect:之外获取一个窗口的graphicscontext,然后随时绘制到那个(参见,例如{,here), 但它听起来像你真正想要做的是截取窗口普通绘图程序的结果并在顶部做一些你自己的东西。您可以通过切换窗口内容视图的类并覆盖drawRect来实现此目的。处理注入的辅助函数看起来像这样:

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

修改

啊,你对整个窗口感兴趣。框架等也是NSView实例,但它们是您无法直接访问的NSView的私有子类。您可以通过调用窗口上的display强制重绘它们,但这可能会覆盖您对窗口所做的任何操作,因为它将使用这些类的现有绘图例程。

所以你可能还想考虑在drawRect:中调用这些视图的drawRect:方法(对[[NSGraphicsContext currentContext] graphicsPort的调用]:将给你一个可以与Quartz API一起使用的CGContextRef)。您可以通过在窗口的内容视图上调用superview来获取帧视图。

请注意,窗口框架视图的排列没有记录,可能会随系统更新而改变。

无论如何,听起来像一个有趣的项目!

答案 2 :(得分:3)

我没有遇到使rect失效的问题,但由于你的问题是如何重绘一个完整的窗口,所以它似乎不是你需要的。

无效时,您告诉系统视图的一部分无效。下次你的系统为绘图腾出时间(通常在声明失效后立即),它会重绘你失效的矩形。

setNeedsDisplay完全相同,除了整个视图而不是该视图中的特定rect。在你的问题中这没关系,因为你想刷新整个窗口。它从UIView链接到Quartz到内部系统,所以你也可以使用Quartz绘图,只要你通过drawRect处理它,在绘图时调用它。

所以只需调用[yourWindow setNeedsDisplay];并且系统会知道你的窗口需要尽快重新绘制。