【问题标题】:Force a window to redraw itself using Core Graphics?使用 Core Graphics 强制窗口重绘自身?
【发布时间】:2011-10-04 12:42:13
【问题描述】:

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

但是,当我注入已经在运行的应用程序时,由于窗口已经被绘制,我不能给它想要的效果。所以,我正在寻找一些可以让我重绘整个窗口的石英/核心图形或一些技术,可以让我发送一些事件/调用一些函数,这将使系统重新绘制整个窗口。

我的意思是要再次绘制窗口上的所有内容,以便执行我的挂钩 API 以创建适当的效果、阴影和颜色。这里创建和绘制窗口的顺序很重要。

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

有人知道我怎样才能做到这一点吗?

【问题讨论】:

  • Mac 上是否有类似 invalidateRect 的东西会强制重绘窗口?

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


【解决方案1】:

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

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

如果你想调用一些神奇的 CGxxx() 函数来重新绘制窗口,这是做不到的。窗口的标题和框架是系统绘制的,但是对于内容,底层的 API 没有办法知道应该在那里绘制什么。唯一知道如何绘制视图的是视图本身。 (也许在窗口的后备存储中缓存了一些东西,但我不知道有任何公共或未记录的 API 可以访问它)。

无论你发现什么都基于要求 NSWindow 对象重绘它的视图。如果你已经被注入到一个进程中,它可能涉及以下步骤:

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

【讨论】:

  • 对不起,我应该添加更多细节。我想要石英解决方案。
  • @MachinTosh 我不确定我是否理解你,但请查看我的更新答案。
  • 您的回答为解决方案提供了一个很好的方法。我一直在寻找一些神奇的 CGxxx(:D),因为我试图达到 Quartz 级别并且我试图避免混乱。
【解决方案2】:

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

可以在 drawRect: 之外获取窗口的图形上下文,然后在需要时绘制到该窗口(参见,例如,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 来获取框架视图。

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

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

【讨论】:

  • 我得到了你答案的第一部分。但不是第二个。因此,CoreGraphics 不会绘制到窗口可可。很公平!但是在第二部分中,您表现出混乱(我不知道这种技术,谢谢)。但是,更合适的解决方案可能是一些如何将 drawRect:] 指令发送到窗口?我正在使用类似于“github.com/comex/inject_and_interpose”的技术。所以它的 C/C++ 代码。你能推荐一些与此相关的东西吗?
  • 我想我可能误解了你在做什么......也许你可以在你的问题中多说一点关于你对你注入的应用程序的窗口做了什么?
  • 我会尝试使用 drawrect 来管理可可应用程序,但我无法使用这些技术管理 Dock、Finder。所以我在 Core-Graphics 中寻找解决方案。
  • 四处挖掘。你可能会发现它们也是某种 NSView 子类。从 10.6 开始,Finder 至少肯定都是基于可可的。如果 google 失败了,FScript Anywhere 是一个很好的工具,可以帮助您找出这类东西......
【解决方案3】:

我还没有遇到使矩形无效的事情,但是由于您的问题是如何重绘一个完整的窗口,所以它似乎并不是您所需要的。

当失效时,你告诉系统你的视图的一部分是无效的。下次您的系统为绘制提供时间时(通常在宣布无效后立即),它将重新绘制您无效的矩形。

setNeedsDisplay 的作用完全相同,只是针对整个视图而不是该视图内的特定矩形。在您的问题中,这并不重要,因为您想刷新整个窗口。它从 UIView 到 Quartz 再到内部系统,所以你的 Quartz 绘图也会被使用,只要你通过绘图时调用的 drawRect 处理它。

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

【讨论】:

    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-21
    • 2010-09-23
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    相关资源
    最近更新 更多