12

Mac OS Xでは、すべてのディスプレイに一意のCGDirectDisplayID番号が割り当てられます。)を使用するCGGetActiveDisplayList([NSScreen screens]、それらにアクセスすることができます。Appleのドキュメントによると:

表示IDは、プロセス間およびシステムの再起動後も保持でき、特定の表示パラメーターが変更されない限り、通常は一定のままです。

新しい2010年半ばのMacBookProでは、Appleは自動切り替えのIntel/nVidiaグラフィックスの使用を開始しました。ラップトップには、2つのGPU、低電力のIntelと高電力のnVidiaがあります。以前のデュアルGPUラップトップ(2009モデル)には自動GPU切り替えがなく、GPU切り替えを行うには、ユーザーが設定を変更し、ログオフしてから再度ログオンする必要がありました。古いシステムでもGPUは1つしかありませんでした。

2010年半ばのモデルには、ディスプレイが1つのGPUから次のGPUに切り替わるときにCGDirectDisplayIDが同じままにならないという問題があります。例えば:

  1. ノートパソコンの電源がオンになります。
  2. 内蔵LCDスクリーンはIntelチップセットによって駆動されます。ディスプレイID: 30002
  3. 外部ディスプレイが接続されています。
  4. 内蔵 LCDスクリーンはnVidiaチップセットに切り替わります。表示IDの変更: 30004
  5. 外部ディスプレイはnVidiaチップセットによって駆動されます。
  6. ...この時点で、Intelチップセットは休止しています...
  7. ユーザーが外部ディスプレイのプラグを抜きます。
  8. 内蔵LCDスクリーンがIntelチップセットに戻ります。表示IDが元に戻ります:30002

私の質問は、GPUの変更によって変更されたときに、古いディスプレイIDを新しいディスプレイIDに一致させるにはどうすればよいですか?


について考えた:

ディスプレイIDが2だけ変わることに気づきましたが、これがすべての新しいMacBook Proに共通するのか、それとも私のものだけなのかを判断するのに十分なテストMacがありません。とにかく、「互いに+/- 2のディスプレイIDをチェックするだけ」が機能する場合は、一種の応急修理です。


試した:

CGDisplayRegisterReconfigurationCallback()表示が変更される前後に通知する、には、一致するロジックがありません。登録されているメソッド内にこのようなものを入れても機能しません。

// Run before display settings change:
CGDirectDisplayID directDisplayID = ...;
io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
CFDictionaryRef oldInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);

// ...display settings change...

// Run after display settings change:
CGDirectDisplayID directDisplayID = ...;
io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
CFDictionaryRef newInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);
BOOL match = IODisplayMatchDictionaries(oldInfoDict, newInfoDict, 0);

if (match)
    NSLog(@"Displays are a match");
else
    NSLog(@"Displays are not a match");

上で起こっていることは次のとおりです。

  1. 表示設定が変更される前にoldInfoDictをキャッシュしています。
  2. 表示設定が変更されるのを待っています
  3. 次に、を使用してoldInfoDictnewInfoDictと比較します。IODisplayMatchDictionaries()
  4. IODisplayMatchDictionaries()BOOLを返します。「はい」は同じか、「いいえ」は異なります。

残念ながら、IODisplayMatchDictionaries()同じディスプレイがGPUを変更した場合、YESは返されません。比較している辞書の例を次に示します(IODisplayLocationキーを見てください)。

// oldInfoDict  (Display ID: 30002)
oldInfoDict: {
    DisplayProductID = 40144;
    DisplayVendorID = 1552;
    IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/IGPU@2/AppleIntelFramebuffer/display0/AppleBacklightDisplay";
}

// newInfoDict  (Display ID: 30004)
newInfoDict: {
    DisplayProductID = 40144;
    DisplayVendorID = 1552;
    IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/P0P2@1/IOPCI2PCIBridge/GFX0@0/NVDA,Display-A@0/NVDA/display0/AppleBacklightDisplay";
}

ご覧IODisplayLocationのとおり、GPUを切り替えるとキーが変わるため、IODisplayMatchDictionaries()機能しません。

DisplayProductID理論的には、キーとキーだけを比較できDisplayVendorIDますが、エンドユーザーソフトウェアを作成していて、ユーザーが2つ以上の同一のモニターを接続している(つまり、両方に同じDisplayProductID / DisplayVendorIDがある)状況が心配です。 。言い換えれば、それは潜在的なグリッチに開かれた完全ではない解決策です。


どんな助けでも大歓迎です!:)

4

4 に答える 4

4

あなたが「試した」とリストしたものよりも概念的に良い方法は見つかりませんでした。しかし、ベンダー ID と製品 ID のみを比較するというあいまいさの問題に対する解決策を見つけました。

oldInfoDictコードには、接続されている各ディスプレイのEDIDを含むキー( IOGraphicsTypes.hnewInfoDictで定義)の追加エントリが含まれています。私の観察によると、このデータは全体として、GPU スイッチ間で永続的なままです。例えば:kIODisplayEDIDKey

    CGDirectDisplayID displayId = [[[screen deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
    io_service_t displayPort = CGDisplayIOServicePort(displayId);

    if (displayPort == MACH_PORT_NULL)
        return nil;  // No physical device to get a name from.

    CFDictionaryRef infoDict = IODisplayCreateInfoDictionary(displayPort, kIODisplayOnlyPreferredName);

    NSData *displayEdid = (NSData *)CFDictionaryGetValue(infoDict, CFSTR(kIODisplayEDIDKey));
    NSLog(@"EDID: %@", displayEdid);

    CFRelease(infoDict);

ウィキペディアのEDIDのデータ記述を見ると、このブロブには既にメーカー、製品 ID、およびシリアルが含まれています。したがって、EDID データを使用してディスプレイを比較するだけで十分です (または、短い数値のみを比較する場合は、そのハッシュなど)。

于 2013-11-20T16:34:25.397 に答える
1

私はプロではありませんが、答えは、ユーザーがディスプレイを変更したときにApple が通知できるようにすることだと思います。コールバックの情報には、CGDirectDisplayIDs を追加および削除するためのフラグが含まれています。

ユーザーは操作中にグラフィックカードを追加または削除してはならないので、起動時にリストを作成して遊んで、「削除」フラグを取得するたびに次の「追加」操作を設定して、リスト内のその ID を置き換えます。

CGDisplayRegisterReconfigurationCallback関数を呼び出すたびに返される情報を印刷してみます。「remove」フラグが設定された DeviceUID を持つものを取得してから、「add」フラグが設定された別の呼び出しを取得するかどうかを確認してください。これらの ID を照合CGGetActiveDisplayListすることも、何が起こっているのかを理解するのに役立ちます。

それが私の最善の策です。

于 2010-05-21T13:50:35.737 に答える