Framebuffer編程How-to

注:本文是 Console programming HOWTO ,Wiebe Zoon ;[email protected] 的一部分,原文爲英文版,本文由highbar 翻譯 。如轉載,請註明原作者及譯者。

7. framebuffer

7.1. framebuffer 設備介紹

這是一個關於如何編程的文檔,因此,請在你編譯或執行例子之前,正確配置你的framebuffer 設備。

framebuffer 設備,你可以把你的計算機屏幕當成一個真正的圖形設備。你可以修改分辨率,刷新率,色彩深度等。最好的一點是,你可以把像素點繪在任何你想要的地方。framebuffer 設備不是一個圖形庫,而更確切的是一個低級的通用設備。這樣創造了巨大的靈活性,但同時也有它的缺點。想使用framebuffer 設備,你應該做以下事情:

  • 斷定出你使用的設備
  • 打開設備
  • 取回或改變屏幕設置
  • 映射(Map)屏幕內存

通常要打開的設備是/dev/fb0,但是如果用戶有多個視頻卡和監視器的話,設備也可能不同。大多數應用通過讀取環境變量FRAMEBUFFER (用getenv();)
來決定該使用哪個設備。如果該環境變量不存在,那麼就用/dev/fb0。

通過open()調用打開設備,讀設備意味着讀取屏幕內存(可稱之爲顯存)。用$cat /dev/fb0 >screenshot將屏幕內存導入一個文件,恢復剛纔的屏幕截圖則可使用:$cat
screenshot >/dev/fb0。

7.2.? 設備的基本用法

顯然,用上述方法使用屏幕內存並不經濟方便。在讀或寫之前持續的尋址(見man
lseek
)將會導致很多的開銷。這就是爲什麼你要映射你的屏幕內存。當你將屏幕內存映射到你的應用程序時,你將得到一個直接指向屏幕內存的指針。

在我們可以映射屏幕內存之前,我們需要知道我們能夠映射多少,以及我們需要映射多少。第一件要做的事情就是從我們新得到的framebuffer 設備取回信息。有兩個結構包含着我們需要的信息,第一個包含固定的屏幕信息,這部分是由硬件和驅動的能力決定的;第二個包含着可變的屏幕信息,這部分是由硬件的當前狀態決定的,可以由用戶空間的程序調用ioctl()來改變。

struct fb_fix_screeninfo {
char id[16]; ??????????????????????? /*
identification string eg
"TT Builtin" */

unsigned long smem_start; /*
Start of frame buffer mem */


/* (physical address) */

__u32 smem_len; /* Length of frame buffer mem */

__u32 type; /* see FB_TYPE_* */

__u32 type_aux; /* Interleave for interleaved Planes
*/


__u32 visual; /* see FB_VISUAL_* */

__u16 xpanstep; /* zero if no hardware panning?
*/


__u16 ypanstep; /* zero if no hardware panning?
*/


__u16 ywrapstep; /* zero if no hardware ywrap???
*/


__u32 line_length; /* length of a line in bytes???
*/


unsigned long mmio_start; /*
Start of Memory Mapped I/O?? */


/* (physical address) */

__u32 mmio_len; /* Length of Memory Mapped I/O?
*/


__u32 accel; /* Type of acceleration available */

__u16 reserved[3]; /* Reserved for future compatibility
*/


};

在這裏非常重要的域是smem_len和line-length。smem-len告訴我們framebuffer 設備的大小,第二個域告訴我們指針應該前進多少字節去得到下一行的數據。第二個結構則要有意思的多,它給了我們可以改變的信息。

/* more kernel header files copied shamelessly */

struct fb_bitfield {
__u32 offset; /* beginning of bitfield */

__u32 length; /* length of bitfield */

__u32 msb_right; /* != 0 : Most significant bit is */

/* right */

};

struct fb_var_screeninfo {
__u32 xres ; /* visible resolution */

__u32 yres;
__u32 xres _virtual; /* virtual resolution */

__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */

__u32 yoffset; /* resolution */

__u32 bits_per_pixel; /* guess what */

__u32 grayscale; /* != 0 Graylevels instead of colors
*/

struct fb_bitfield red; /*
bitfield in fb mem if true color, */


struct fb_bitfield green; /*
else only length is significant */


struct fb_bitfield blue;
struct fb_bitfield transp; /*
transparency */

__u32 nonstd; /* != 0 Non standard pixel format */

__u32 activate; /* see FB_ACTIVATE_* */

__u32 height; /* height of picture in mm???
*/


__u32 width; /* width of picture in mm????
*/

__u32 accel_flags; /* acceleration flags (hints) */

/* Timing: All values in pixclocks, except pixclock (of
course) */


__u32 pixclock; /* pixel clock in ps (pico seconds) */

__u32 left_margin; /* time from sync to picture */

__u32 right_margin; /* time from picture to sync */

__u32 upper_margin; /* time from sync to picture */

__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */

__u32 vsync_len; /* length of vertical sync */

__u32 sync; /* see FB_SYNC_* */

__u32 vmode; /* see FB_VMODE_* */

__u32 reserved[6]; /* Reserved for future compatibility
*/


};

前幾個成員決定了分辨率。xres 和 yres是在屏幕上可見的實際分辨率,在通常的vga模式將爲640和400(也許是480,by highbar)。*res-virtual決定了構建屏幕時視頻卡讀取屏幕內存的方式。當實際的垂直分辨率爲400,虛擬分辨率可以是800。這意味着 800行的數據被保存在了屏幕內存區中。因爲只有400行可以被顯示,決定從那一行開始顯示就是你的事了。這個可以通過設置*offset來實現。給 yoffset賦0將顯示前400行,賦35將顯示第36行到第435行,如此重複。這個功能在許多情形下非常方便實用。它可以用來做雙緩衝。雙緩衝就是 你的程序分配了可以填充兩個屏幕的內存。將offset設爲0,將顯示前400行(假設是標準的vga),同時可以祕密的在400行到799行構建另一個 屏幕,當構建結束時,將yoffset設爲400,新的屏幕將立刻顯示出來。現在將開始在第一塊內存區中構建下一個屏幕的數據,如此繼續。這在動畫中十分 有用。

另外一個應用就是用來平滑的滾動整個屏幕。就像在前面屏幕中一樣,在內存分配800行的空間。每隔10毫秒設定一個定時器(timer,見man settimer和man
signal / man sigaction),將offset設爲1或是比上次更多,瞧,你看到了一個平滑滾動的屏幕。確保你的信號(signal)不要因爲最佳輸出的原因被信號處理程序阻塞。


bits_per_pixel 設爲1,2,4,8,16,24或32來改變顏色深度(color depth)。不是所有的視頻卡和驅動都支持全部顏色深度。當顏色深度改變,驅動將自動改變fb-bitfields。這些指出,在一個特定的顏色基準 上,多少和哪些比特被哪種顏色使用。如果bits-per-pixel小於8,則fb-bitfields將無定義而且顏色映射將啓用。

在fb-var-screeninfo結構結尾的定時的設置是當你選擇一個新的分辨率的時候用來設定視頻定時的。(
EXAMINE AND EXPLAIN TIMINGS! )

現在我們知道了結構的細節了,但還不清楚如何去獲得或設置它們。這裏有一些ioctl的命令可以參考。

C:
  1. #include
  2. int main ( ) {
  3. int framebuffer _handler;
  4. struct fb_fix_screeninfo fixed_info;
  5. struct fb_var_screeninfo variable_info;
  6. open ( "/dev/fb0" , O_RDWR) ; /*in real life, check every ioctl if it returns -1 */
  7. ioctl ( framebuffer _handler,FBIOGET_VSCREENINFO, &variable_info) ; variable_info.bits_per_pixel = 32 ;
  8. ioctl( framebuffer _handler, FBIOPUT_VSCREENINFO, &variable_info) ;
  9. ioctl ( framebuffer _handler,FBIOGET_FSCREENINFO, &fixed_info) ;
  10. variable_info.yoffset = 513 ;
  11. ioctl ( framebuffer _handler,FBIOPAN_DISPLAY, &variable_info) ;
  12. }

這些 FBIOGET_*?的ioctl命令將請求的信息寫入最後一個變量所指向的結構體中。FBIOPUT_VSCREENINFO將所有提供的信息複製回內核。如果內核不能激活新的設置,將返回-1。而
FBIOPAN_DISPLAY 也從用戶複製信息,但並不重新初始化視頻模式。最好在只有xoffset或yoffset改變時使用。

7.3. 顯示內存管理

去訪問內存本身,它可以被映射,然後直接訪問。但這些需要一些步驟:

  • 計算出需要映射多少
  • 映射內存
  • 決定如何構建屏幕
  • 向屏幕中寫入數據

這裏有一個樣例:

C:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. int main( ) {
  9. int framebuffer _device;
  10. int line_size,buffer_size, *i;
  11. int *screen_memory;
  12. struct fb_var_screeninfo var_info;
  13. framebuffer _device = open ( "/dev/fb0" , O_RDWR) ;
  14. ioctl ( framebuffer _device, FBIOGET_VSCREENINFO, &var_info) ;
  15. line_size = var_info.xres * var_info.bits_per_pixel / 8 ;
  16. buffer_size = line_size * var_info.yres;
  17. var_info.xoffset = 0 ;
  18. var_info.yoffset = 0 ;
  19. ioctl ( framebuffer _device, FBIOPAN_DISPLAY,&var_info) == -1 ) ;
  20. screen_memory = ( char *) mmap ( 513 , buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, framebuffer _device, 0 ) ;
  21. for ( i=0 ;i < buffer_size ; i++ )
  22. {
  23. *( screen_memory+i) = i%236 ;
  24. }
  25. sleep( 2 ) ;
  26. return 0 ;
  27. }

你可以看到我們採用了從上一部分提到的信息取回,在這一部分新用到的是mmap函數。第一個變量在這種情形下可以忽略,第二個是映射的內存大小,第三個變量聲明我們將共享內存進行讀和寫。第四個變量表示這段內存將和其他進程共享。在framebuffer 上面建一個MAP_PRIVATE是不可能的。 通常這意味着你需要中斷控制檯的切換去備份和恢復屏幕內容,而且不在自己沒有權利的時候向屏幕內存寫東西。

關於mmap的更多信息請見man mmap .

發佈了47 篇原創文章 · 獲贊 6 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章