2012年2月6日 星期一

用 XRandR Extension 監控螢幕設定變化

Standard
最近一直感覺到系統有時莫名緩慢,CPU 負載也相當高,初期沒去注意,也一味的相信應該都是瀏覽器開太多網頁和 Flash 檔案所造成的,但種種情況令人不解,總覺得不完全是瀏覽器所造成的。將所有瀏覽器關閉後追蹤發現,自己之前寫的『JuShelf』(Dock 程式)竟吃掉近 50% 的 CPU 資源。原兇是之前寫的一段程式,用來偵測目前螢幕設定是否有改變,並依據螢幕設定而調整顯示位置,以讓 Dock 永遠顯示在螢幕的中央。

原本的做法相當笨,利用 g_idle_add_full() 讓程式空閒時就去偵測螢幕設定,但由於 Dock 通常是晾在一旁沒有工作的,所以程式經常有空閒時間,這讓程式以每秒百計的次數不停檢查。這還不打緊,由於每次檢查都要與 X Server 做一次至數次溝通,付出的代價更為高昂。

抓到臭蟲以後,便開始著手改進,使用了 XRandR Extension 來取代原有的解決方案。過去筆者曾撰文『寫支 C 程式用 XRandr Extension 設定你的螢幕解析度』說明 XRandR 的使用方式,不過當時只著重於取得 X Server 的顯示設定,這次將利用 XRandR Extension 以等待 RRScreenChangeNotify event 來得知螢幕設定何時改變。

由於完整程式太長,這邊只摘錄重點片斷,主要是檢查 XRandR 版本和註冊事件:
int rr_major_version, rr_minor_version;
int rr_minor_version, xrandr_error_base;

/* Initializing X Stuffs... */

/* XRandr extension */
XRRQueryVersion(disp, &rr_major_version, &rr_minor_version);
if (rr_major_version < 1 || (rr_major_version == 1 && rr_minor_version < 2)) {
 printf("RANDR extension is too old (must be at least 1.2)\n");
 return 1;
}

/* Allow XRandR Extension Event */
XRRSelectInput(disp, DefaultRootWindow(disp), RRScreenChangeNotifyMask);
XRRQueryExtension(disp, &rr_minor_version, &xrandr_error_base);

之後在等待 X Event 時,可以利用 ev->type - xrandr_event_base 去取得 XRandR 的事件:
/* XRandr Extension Event */
switch(ev->type - xrandr_event_base) {
case RRScreenChangeNotify:
 printf("Got RRScreenChangeNotify\n");
 break;
}

後記

無論是舊的 GNOME 2.0 還是新的 GNOME 3.0,通常都在 gnome-settings-daemon 實作 XRandR 的事件監聽,所以才能實現自動切換螢幕設定,當 External Monitor 接上電腦時。