2012年7月4日水曜日

セカンダリモニタでフルスクリーン

libQGLViewerライブラリでOpenGLのフルスクリーンが簡単にできると知って飛びついてみたものの,セカンダリモニタへのフルスクリーンには対応していなかった.

Webで調べると一番多いのがGetSystemMetrics()に入れる引数を駆使して, セカンダリモニタのサイズを計算しようとするものであるが, GetSystemMetrics()で取得できるのは全てのモニタを含む大きな枠の座標のみで, 2番目以降のモニタのスクリーンサイズは取得できない. そのため,スクリーンサイズの計算は間接的で, 直ぐに破綻する何らかの仮定(例えばスクリーンのアスペクト比が固定値など)が必要. せっかく調べて実装するのだから,直接スクリーンサイズを得る方法を探した.

正解は,EnumDisplayMonitors()とGetMonitorInfo()を使ってスクリーンサイズを取得し, SetWindowLong()で枠をなくし,SetWindowPos()でスクリーンにぴったり張り付くように配置する方法である. 今回Qtを使って実装していたので下記のようにまず, ウィンドウのデバイスコンテキストを取得するコードになっています.

RECT bounds;
 
//callback function from EnumDisplayMonitors()
BOOL CALLBACK MyInfoEnumProc(
    HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    MONITORINFOEX mInfo;
    mInfo.cbSize = sizeof(MONITORINFOEX);
    GetMonitorInfo(hMonitor,&mInfo);
    
    if(mInfo.dwFlags == 0)
    {
        //bounds is global variable available only in this file
        bounds = mInfo.rcMonitor;
    }
    return TRUE;
}
 
// set full screen in the second monitor
void ProjectorWindow::setFullScreen(bool fullScreen)
{
    int n_monitors = GetSystemMetrics(SM_CMONITORS);
    if(n_monitors < 2){
        return;
    }
 
    EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0);
 
    // Obtaining a handler for the window of the current context
    // (in English: getting a reference to the current OpenGL screen)
    HDC dc = GetDC(this->winId());
    HWND hwnd = WindowFromDC(dc);
 
    SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_VISIBLE); 
 
     // Placing the window with the bigger dimensions to cover the whole
    // multi-monitor screen
    SetWindowPos(hwnd, NULL, bounds.left, bounds.top, bounds.right - bounds.left + 1,
            bounds.bottom - bounds.top + 1, SWP_FRAMECHANGED);
}

ただ,モニタの情報が欲しいだけで何で関数ポインタなぞ使わないといけないようにな設計になっているのか理解出来ない.MSDNの説明も相変わらず分かりにくいし.早くMicrosoftから独立しなければ!!

http://msdn.microsoft.com/ja-jp/library/dd162610(v=VS.85).aspx