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

发布于 2024-12-08 00:22:15 字数 454 浏览 1 评论 0原文

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

但是,当我注入已经运行的应用程序时,我无法给它带来所需的效果,因为窗口已经被绘制了。所以,我正在寻找石英/核心图形中的一些东西,它可以让我重绘整个窗口,或者某种技术可以让我发送一些事件/调用一些函数,这将使系统再次重新绘制整个窗口。

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

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

有谁知道我怎样才能实现这一目标?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

愁以何悠 2024-12-15 00:22:15

-[NSView setNeedsDisplayInRect:]-[NSView setNeedsDisplay:]invalidateRect 的直接等效项。

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

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

无论您发现什么,都必须基于要求 NSWindow 对象重绘其视图。如果您已经注入到进程中,则可能涉及以下步骤:

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

-[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
淡笑忘祈一世凡恋 2024-12-15 00:22:15

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

可以在 drawRect: 之外获取窗口的图形上下文,然后在需要时绘制到该图形上下文(例如,参见 此处),
但听起来您真正想要做的是拦截窗口普通绘图例程的结果,并在上面做一些您自己的事情。您可能可以通过切换窗口内容视图的类并覆盖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 来获取框架视图。

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

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

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!

暮光沉寂 2024-12-15 00:22:15

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

失效时,您告诉系统您的视图的一部分无效。下次您的系统有时间进行绘制时(通常在声明无效后立即),它将重新绘制您无效的矩形。

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

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

I haven't encountered something that invalidates a rect, but since your question is how to redraw a complete window, it doesn't seem to be what you need anyway.

When invalidating, you tell the system a part of your view is invalid. The next time your system makes time available for drawing (usually instantly after declaring the invalidation), it will redraw the rect you invalidated.

setNeedsDisplay does exactly the same, except to an entire view and not a specific rect inside that view. In your question this doesn't matter, since you want to refresh the whole window. It chains down from UIView to Quartz to internal system, so your Quartz drawing will be used as well, as long as you handle it through drawRect, which is called when drawing.

So simply call [yourWindow setNeedsDisplay]; and the system will know your window needs to be redrawn as soon as possible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文