在 X window 下要有半透明視窗效果,會需要至少

  • XComposite extension
  • XDamange extension
  • XFixes extension

用這些 extension 做半透明視窗功能的程式,就叫 Composite Manager (也是 X window client)。在如 metacity, mutter, compiz, kwin 中都有實現。
上面的 extension 中,一定要有 XComposite。它可以告訴 X window server 說:嘿,請把這個 window 的內容給我。那 X window server 就會把該 window 繪製動作 redirect (而非直接輸出到畫面上),composite manager 拿到手的會是 Pixmap,接下來,就看你自己怎麼去畫它了。
題外話,如果 X client 是執行於 full screen mode 之下,這時候就不會有 window overlay 的情況 (沒有 overlay,也就看不出來視窗的半透明了),可以考慮不進行 redirect 以增加 performance。這種情況發生於例如 video playback,gaming。這也是 Phronix 測試 Ubuntu, KUbuntu 時,Ubuntu 有 performance drop 的來源。
回到正題。Composite manager 拿到手的 Pixmap (window 的內容),在內容更新時會同步,在 window size change 時則會産生新的 Pixmap,這時候舊的就看 composite manager 要 free 掉什麼的都 OK。這個 window 的內容,如果沒有 alpha channel (透明度資訊) 那畫出來也就不會有透明的效果。
那 X client 在繪製時,如何才會帶 alpha channel 資訊呢?有幾點要注意的

  1. Create window 時,要指定 class 為 TrueColor 的 visual,depth 為 32
  2. Create window 時,attribute 需指定新的 colormap。這個新的 colormap,通常會是用 XCreateColormap() 對同一個 visual 産生的
  3. Create window 時,因為 visual 不同了,所以,background color 及 border color 一定要指定,否則會出現 BadMatch error。這部份 document 上沒提,害我找好老半天

下面是範例。首先 open display 然後取得 screen & root window#include #include
Display * display = XOpenDisplay(NULL);Screen * screen = DefaultScreenOfDisplay(display);Window root = DefaultRootWindow(display);
接下來取得 screen 中 support ARGB 的 visual。template 中放的是過濾條件。找不到合條件的會傳回 NULLXVisualInfo template = { .screen = 0, .depth = 32, .class = TrueColor, .red_mask = 0xff0000, .green_mask = 0xff00, .blue_mask = 0xff,};int n_visual_infos;XVisualInfo * visual_infos = XGetVisualInfo( display, VisualScreenMask | VisualDepthMask | VisualClassMask | VisualRedMaskMask | VisualGreenMaskMask | VisualBlueMaskMask, & template, & n_visual_infos);if(! visual_infos) { g_error("No RGBA visual found");}XVisualInfo visual_info = * visual_infos;XFree(visual_infos);
取得 visual 後就可以産生新的 colormapColormap colormap = XCreateColormap(display, root, visual_info.visual, AllocNone);if(None == colormap) { g_error("Failed to create colormap");}
準備完畢。現在來建立 windowXSetWindowAttributes attrs = { .background_pixel = 0x7fff0000, .border_pixel = 0, .colormap = colormap,};Window win = XCreateWindow( display, root, 0, 0, 720, 480, 0, // border width visual_info.depth, // depth InputOutput, // class visual_info.visual, // visual CWBackPixel | CWBorderPixel | CWColormap, // valuemask & attrs); //& attrs);if(None == win) { g_error("Failed to create window");}XMapWindow(display, win);XFlush(display);
g_usleep(3 * G_USEC_PER_SEC);
執行後,應該會看到一個紅色半透明視窗 show 在畫面上。
如果想用 Gtk+ 做出半透明視窗會更簡單些 (一定要的啦~)。下面的 code 用 vala 寫簡單些using Gtk;using Gdk;
int main(string[] args){ Gtk.init(ref args); Gdk.Screen screen = Gdk.Screen.get_default(); if(! screen.is_composited()) { error("Screen donsen't support composite which is mandatory"); } Gdk.Visual visual = screen.get_rgba_visual(); if(null == visual) { error("Sorry, your screen dosen't support ARGB visual"); }
Gdk.RGBA bg = { 0.5, 1.0, 0.0, 0.0 };
Gtk.Window win = new Gtk.Window(Gtk.WindowType.TOPLEVEL); win.set_visual(visual); win.override_background_color(Gtk.StateFlags.NORMAL, bg); win.show(); Gtk.main();}
一樣,關鍵在於 visual 的設定。
對了,visual 在 X window 中,window create 之後就沒法改變了,不過 colormap 則 OK (但要從同一個 visual 産生,否則 BadMatch error)。所以,用 XLib 寫 code 時 visual 要在 XCreateWindow() 時指定 (廢話),使用 Gtk+ 時,則要在 new 出 widget 後,realize 前,把 visual 設上,才有效果。