2010年12月12日 星期日

寫支 C 程式用 XRandr Extension 設定你的螢幕解析度

Standard
在 X11 下取得螢幕顯示大小,最快的方式就是得到當前的 Screen ,然後用 DisplayWidth() 和 DisplayHeight() 得到寬高,如果是在高階的圖形介面架構下開發應用程式,如:GTK+,也有相關的 API 可以取得 display 的大小。然而,要是我們想取得更多關於螢幕和顯示晶片所支援的模式,便要引入 XRandr Extension 來達成。

若不了解 XRandr 是什麼,可以開啟終端機(Terminal)程式,接著輸入 xrandr 指令,就會看到顯示器的相關訊息,除了可以得到有幾種螢幕可以用,還分別可以看到支援的模式(解析度和掃描頻率),以下是在筆者 X200t (有外接一台支援 1920x1080 的 24" 顯示器)上執行的結果:
$ xrandr
Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 8192 x 8192
VGA1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 531mm x 299mm
   1920x1080      60.0*+   60.0     60.0  
   1440x900       59.9  
   1280x800       59.8  
   1152x864       75.0  
   1024x768       70.1     60.0  
   800x600        60.3     56.2  
   640x480        66.7     60.0  
   720x400        70.1  
LVDS1 connected (normal left inverted right x axis y axis)
   1280x800       60.0 +
   1024x768       60.0  
   800x600        60.3     56.2  
   640x480        59.9  
HDMI1 disconnected (normal left inverted right x axis y axis)
DP1 disconnected (normal left inverted right x axis y axis)
HDMI2 disconnected (normal left inverted right x axis y axis)
DP2 disconnected (normal left inverted right x axis y axis)
DP3 disconnected (normal left inverted right x axis y axis)
註:Xrandr 除了得到關於當前顯示器的設定之外,也可以設定現在的模式,更多功能可以參考『xrandr --help』的說明。

當然,XRandr Extension 還支援旋轉畫面、多螢幕顯示等控制,不過不在本文討論範疇。這裡,我們要用 C 語言先來實作一支程式,用 XRandr API 得到目前解析度以及列出顯示器支援的所有模式。

listmode.c 完整程式碼:
#include <stdio.h>
#include <X11/X.h>
#include <X11/extensions/Xrandr.h>

void main()
{
    XRRScreenSize *sizes;
    XRRScreenConfiguration *sc;
    int nsize;
    short rate;
    Rotation current_rotation;
    SizeID size_id;

    int i;

    Display *dpy = XOpenDisplay(NULL);
    int screen = DefaultScreen(dpy);
    Window root = RootWindow(dpy, screen);

    /* Get XRandr Screen Configuration */ 
    sc = XRRGetScreenInfo(dpy, root);

    /* Get all Modes of Screen */
    sizes = XRRConfigSizes(sc, &nsize);
    printf("nsize: %d\n", nsize);

    /* Get Current Rate */
    rate = XRRConfigCurrentRate(sc);
    printf("rate: %d\n", rate);

    /* Get  current mode ID and rotation config */
    size_id = XRRConfigCurrentConfiguration(sc, ¤t_rotation);
    printf("size id: %d\n", size_id);

    for (i = 0; i < nsize; i++) {
        if (size_id == i)
            printf("%d %dx%d *\n", i, sizes[size_id].width, sizes[size_id].height);
        else
            printf("%d %dx%d\n", i, sizes[i].width, sizes[i].height);
    }
}

編譯:
gcc -o listmode listmode.c -lX11 -lXrandr

執行結果(在筆者電腦上 1920x1080 60Hz 為當前的解析度模式):
$ ./listmode
nsize: 8
rate: 60
size id: 0
0 1920x1080 *
1 1440x900
2 1280x800
3 1152x864
4 1024x768
5 800x600
6 640x480
7 720x400

若要更進一步更改和設定模式,可在範例程式碼最後加上 RRSetScreenConfigAndRate():
XRRSetScreenConfigAndRate(dpy, sc, root, (SizeID)0,
    current_rotation, rate, CurrentTime);
註:其中 SizeID 型態指的是模式的編號,同列表中最前面的編號,可擇一使用。