2011-10-04 16 views
6

私はインジェクションシステムを開発しており、いくつかの石英APIを使ってMac OS X上のウィンドウでいくつかの素晴らしいエフェクトを作成しています。たとえば、ユーザーがウィンドウ内の赤に赤を設定すると、Core Graphicsを使用してウィンドウを強制的に再描画しますか?

しかし、私はすでに実行されているアプリケーションに注入したときに、ウィンドウがすでに描かれているとして、私はそれが効果を希望与えることはできません。ですから、私はクォーツ/コアグラフィックスで何かを探しています。これは、ウィンドウ全体を再描画するか、イベントを送信できるいくつかのテクニックを呼び出すことができます。

私は、ウィンドウ上のすべてのものが私のフックのAPIのは、適切な効果、色合いや色を作成するために実行されるように再描画される意味。ここでウィンドウを作成する順序は&が重要です。私はinject&interposeと同様の手法や注射のコードを使用しています

は、C/C++コードです。

誰がどのように私はこれを達成することができますアイデアを持っていますか?

+2

MacにinvalidateRectのようなウィンドウを強制的に再描画するようなものがありますか? – MacGeek

答えて

5

-[NSView setNeedsDisplayInRect:]および-[NSView setNeedsDisplay:]は、invalidateRectの直接等価物である。

私はあなたがクォーツ/ CoreGraphicsでそれを必要とすることによって、何を意味するのか分かりません。ココアはすでにそれらを描画に使用しています。

あなたは、ウィンドウが再び塗らようになりますいくつかの魔法CGxxx()関数を呼び出したい場合は、それを行うことができません。ウィンドウのタイトルとフレームはシステムによって描画されますが、コンテンツに関しては、下位レベルのAPIが何をそこに描画すべきかを知る方法はありません。ビューを描画する方法を知っている唯一の人は、ビュー自体です。 (おそらく、ウィンドウのバッキングストアにキャッシュされているものがありますが、アクセスするための公開APIまたは文書化されていないAPIはわかりません)。

NSWindowオブジェクトにビューの再描画を依頼することに基づいて、にはがあります。すでにプロセスに注入している場合、それは次のステップを伴うことがあります。

  • 位置OBJ-Cランタイム
  • +[NSApplication sharedApplication]-[NSApplication windows]を使用して
  • NSApplicationのクラスを見つける(あなたは少なくともobjc_msgSend機能が必要になります)
+0

申し訳ありませんが、詳細を追加する必要があります。私は石英で解決策が欲しい。 – MacGeek

+0

@MachinTosh私はあなたを理解しているとは分かりませんが、私の更新された答えを見てください。 – hamstergene

+0

あなたの答えは解決策に向けた良いアプローチです。私はいくつかの魔法CGxxx(:D)を探していました。なぜなら私はクォーツレベルにいようとしていて、私はうんざりを避けようとしていたからです。 – MacGeek

5

を再描画するcontentViewdisplay等を用いNSWindow*オブジェクトポインタ

  • を見つけるために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:メソッドをswizzling(drawRect:[NSGraphicsContext currentContext] graphicsPortの呼び出し)すると、Quartz APIで使用できるCGContextRefが得られます)。ウィンドウのコンテンツビューでsuperviewを呼び出すと、フレームビューを取得できます。

    ウィンドウのフレームビューの配置は文書化されておらず、システムのアップデートによって変更される可能性があることに注意してください。

    とにかく面白いプロジェクトのような音です。

  • +0

    私はあなたの答えの最初の部分を取得します。しかし、2番目ではありません。したがって、CoreGraphicsはウィンドウのココアに描画しません。けっこうだ!しかし、2番目の部分では、私はこのテクニックを知りませんでした(ありがとう)swizzlingを示した。しかし、より適切な解決策は、どのようにdrawRect:]命令をウィンドウに送信するのだろうか?私は 'https://github.com/comex/inject_and_interpose/'に似たテクニックを使用しています。そのC/C++コード。あなたはこれに関連する何かを提案できますか? – MacGeek

    +0

    私はあなたがやっていることを誤解しているかもしれないと思います...あなたはあなたがアプリケーションのウィンドウに何をしているのかについてあなたの質問にもう少し言えるかもしれませんか? –

    +0

    私は質問を更新しました。 – MacGeek

    3

    私はrectを無効にする何かが発生していませんが、あなたの質問は完全なウィンドウを再描画する方法なので、とにかく必要なものではないようです。

    無効にすると、ビューの一部が無効であるとシステムに通知します。次にシステムが描画に使用できる時間を設定すると(通常は無効を宣言した直後に)、無効化したrectを再描画します。

    setNeedsDisplayは、ビュー全体を除いて、まったく同じであり、そのビュー内の特定の矩形を除きます。あなたの質問では、これは重要ではありません。ウィンドウ全体をリフレッシュしたいからです。これはUIViewからQuartzにチェーン接続されているため、描画時に呼び出されるdrawRectを通して処理する限り、Quartzの描画も同様に使用されます。

    [yourWindow setNeedsDisplay]を呼び出します。できるだけ早くウィンドウを再描画する必要があることをシステムが認識します。

    関連する問題