DirectFB編程

1、簡介react

DirectFB是一個輕量級的提供硬件圖形加速,輸入設備處理和抽象的圖形庫,它集成了支持半透明的視窗系統以及在LinuxFramebuffer驅動之上的多層顯示。它是一個用軟件封裝當前硬件沒法支持的圖形算法來完成硬件加速的層。DirectFB是爲嵌入式系統而設計。它是以最小的資源開銷來實現最高的硬件加速性能。linux

 

DirectFB的組成ios

一、基本庫函數git

這部分代碼在lib目錄下,它分爲三個部分:算法

direct: 裏面是一些公共函數,其中包括哈希表、鏈表、線程、調試信息、signal處理、優化過的memcpy和平臺相關的一些函數。 
fusion:它有兩個版本,一個是針對單進程的,要求全部應用程序在一個進程中運行,這相對來講比較簡單。另一個是針對多進程的,應用程序能夠在多個進程中運行。它實現了一些進程間通訊機   制,其中包括互斥、共享內存、共享內存中的vector實現、帶引用計數的內核對象和reactor等。多進程版本還須要一個內核模塊linux-fusion的支持。 
voodoo: 不清楚(若那位高手知道,請補充一下,謝謝)。

二、對第三方組件庫的包裝編程

這部分代碼在interfaces目錄下。Interfaces可能會引發別人的誤解,由於它並非DFB對外提供的接口,而是把第三方組件歸入DFB的接口。它包括三類:session

字體:字體有點陣字體和矢量字體之分,矢量字體又有諸如truetype之類幾種格式。前者可能比較簡單,然後者的處理至關複雜,要藉助如freetype等第三方程序庫來實現。
     DFB定義了IDirectFBFont接口來處理字體,在第三方字體程序庫上加上一個adapter就能夠在DFB中使用了。 
圖片:圖片格式的種類不少,像BMP之類的位圖處理可能比較簡單,而像JPG和PNG等的圖片,採用了高級的壓縮技術,解壓算法比較複雜,一般須要第三方程序庫的支持。
     DFB定義了IDirectFBImageProvider接口來處理圖片,在第三方圖片程序庫上加上一個adapter就能夠在DFB中使用了。 
視頻:視頻格式更多,解壓算法也更復雜,天然也要藉助第三方庫來實現。
     DFB定義了IDirectFBVideoProvider接口來處理視頻,在第三方視頻程序庫上加上一個adapter就能夠在DFB中使用了。

三、核心代碼架構

這部分代碼在src目錄下。它能夠分爲兩大類:
核心組件。DFB的core由多個部分組成,每一個部分稱爲一個core_part,都實現同一個接口CorePart。這個接口並不描述它們的功能,而是用於管理的。初看這些函數時,可能會感到有些奇怪。最好要先了解DFB採用的master/slave模型:第一個運行應用程序是master進程,後來運行的應用程序是slave進程。master進程負責初始化和~初始化arena,它只能在全部slave退出以後才能退出。而slave進程則能夠隨時加入arena,也能夠隨時退出arena。框架

核心組件包括下面幾個組件:ide

dfb_core_clipboard: 剪切板。 
dfb_core_colorhash:調色板。 
dfb_core_gfxcard:圖形卡,主要完成基本的繪圖功能,如繪直線、填充等等。 
dfb_core_input:輸入設備。 
dfb_core_layers:分層功能,好像要硬件支持,一般都只有一個層。 
dfb_core_screens:邏輯屏幕(可能像X同樣支持多個屏幕吧,不太清楚,有時間再研究)。 
dfb_core_system:顯示輸出,把gfxcard繪製後的圖形數據輸出到屏幕上,便可以經過fbdev輸出到本機屏幕上,也能夠經過sdl/x11/vnc輸出到遠程主機的屏幕上。對於像sdl/x11等,也包括對輸入事件的處理。 
dfb_core_wm:窗口管理器。
以上這些core_part,有的是直接實現的,好比clipboard。有的只是一層包裝,具體的實如今一個獨立的共享庫中,在運行時經過參數來控制加載具體的實現,如system。

對外接口。這主要是給上層應用程序使用的。其中包括

IDirectFBInputDevice: 輸入設備 
IDirectFBScreen: 屏幕。 
IDirectFBSurface: 繪圖表面。 
IDirectFBPalette: 調色板。 
IDirectFBFont: 字體 
IDirectFBImageProvider:圖片 
IDirectFBVideoProvider:視頻 
IDirectFBWindow:窗口 
DirectFBEventBuffer: 事件緩衝

四、窗口管理器

這部分代碼在wm目錄下。DFB實現了兩個窗口管理器。

default:實現了基本的窗口管理功能,支持一些快捷鍵。 
unique:功能也很弱,不過架構還能夠,加入本身的功能很方便。

五、 輸入設備

這部分代碼在inputdrivers目錄下。其實這些代碼並非真正的驅動,只是一個adapter層,它把從linux設備文件讀到的事件,轉換成DFB本身的事件格式,而後調用dfb_input_dispatch把事件分發出去。

六、輸出設備

這部分代碼在system目錄下。這也是一個adapter層,主要對顯示設備的抽象,有的也包括對輸入事件的處理。其中包括:

fbdev: 輸出到frame buffer。 
osx:   輸出到mac os上。 
vnc:  輸出到Virtual Network Computing(相似於微軟遠程桌面的一個協議)。 
x11:  輸出到X Window上,在0.9.24仍然有問題,建議使用SDL。 
sdl:  輸出到Simple DirectMedia Layer。

 

DirectFB中的重要術語

一、Blitting
Blitting是在拷貝圖像數據的進程中所引用。舉一個最簡單的例子就是當兩個Surface有相同的大,顏色深度和像素格式時Blitting其中一個Surface到另外一個Surface。在這個過程當中內存只被複製而沒有被處理(就像複製其餘任何類型的數據同樣)。alpha通道的傳輸,或者從一種像素格式到另外一種像素格式的傳輸。許多圖形顯卡包含了一個硬件Blitting來完成多種格式的傳輸。

二、Surface
Surface是內存中一個圖像以一種具體的像素格式被保存的一塊保留區域。一個Surface能夠位於視頻和/或系統內存中。能夠在一個Surface上進行畫圖操做或者把一個Surface Blitting到另外一個。(見1.21節)
在全屏模式下時,屏幕中的可視區表示爲」主Surface」,因此能夠直接在屏幕的可視區完成圖形操做。
每一個Surface均可以選擇雙緩衝,圖形操做將首先在輔助緩衝區中執行而後在Flip()被調用以後變得合法。在許多狀況下建議在主Surface中使用雙緩衝來防止閃爍。

三、SubSurface
SubSurface使用和正規Surface相同的接口。它表明父類Surface的一個部分而且沒有爲本身分配任何系統或視頻存儲空間。

四、Layer
依靠於圖形硬件能夠有一個或者多個顯示層。一個標準的PC顯卡只有一個層,可是,就像機頂盒就可能支持2個或更多的層。不一樣的層在顯存中佔據着不一樣的區域,一般經過alpha混合來組合,這由顯示硬件自動完成。若是最底層的內容發生了改變將不會被重繪,上一層的內容保持不變。今天,許多PC顯卡也支持額外的能夠縮放能夠從YUV轉爲RGB的層(視頻層)。這個層不能進行顏色混合和設置成保持徹底的不透明。variosDirectFB圖形驅動支持視頻層。

五、Window / Windowstack
一般一個層的surface的內容受控於集成的窗體系統,這意味着屬於這個層的窗體在一個可配置的背景上。每一個窗體有它本身的一個surface,這個surface 被窗體系統用來生成構成重疊窗體的圖像。

六、reactor模式
DFB中的消息,不管是進程內的,仍是進程間的,都是經過reactor來傳遞的。這是一種簡單的發佈-訂閱機制,誰關心誰就註冊。不先弄清楚reactor的機制,很容易就被消息的流向搞糊塗了。

七、加鎖術語。加鎖/解鎖動做經常使用lock/unlook、acquire/release、 wait/release等術語,而DFB裏使用skirmish_prevail/skirmish_dismiss。

八、引用計數術語。增長/減小引用計數經常使用ref/unref、addref/release等術語。DFB裏使用了ref/unref,同時增長了幾個動做:link/unlink用於增長和減小全局引用計數,ref增長的計數,只要應用程序退出,fusion自動釋放這個引用計數。而link增長的引用計數非要用unlink減小才行,應用程序退出時不會自動減小。inherit: 繼承另一個對象的引用計數,即把被繼承的對象的引用計數加到繼承者身來,不但如此,當被繼承的對象的引用計數增減時,自動增減繼承者的引用計數。

九、註冊/註銷術語。註冊/註銷經常使用register/unregister等術語,DFB使用了attach/detatch。

十、內核對象的宏。DFB用宏FUSION_OBJECT_METHODS去實現一個fusion 十一、object的子類,FUSION_OBJECT_METHODS是在object.h裏定義的。像CoreWindow和CoreSurface等內核對象,都調用這個宏去實現本身的方法。

十二、CorePart的宏。DFB用宏DFB_CORE_PART去實現一個core part,DFB_CORE_PART是在core_parts.h定義的。

1三、動態模塊的加載。DFB並不要求動態加載模塊實現特定的接口函數,而當模塊被加載時(dlopen時),把本身安裝到框架中。模塊使用了gcc的__attribute__((constructor))擴展,模塊被加載時,該函數自動執行,而後調用direct_modules_register註冊本身。

1四、cardstate:我開始被blit函數弄糊塗了,並不像WIN32下那樣要求指明源和目標。後來才知道它是調用另外的函數去設置源和目標,而不是經過參數來指定。

   

 

2、編譯與安裝

一、資源下載

需下載以下資源

DirectFB:DirectFB-1.6.1.tar.gz,
Fusion:   linux-fusion-8.10.2.tar.gz 
Example:  DirectFB-examples-1.6.0.tar.gz

DirectFB 提供3種形式的下載方式: git, cvs 和壓縮包。

地址:http://directfb.org/
或  :git clone git://git.directfb.org/git/directfb/core/DirectFB.git
     git clone git://git.directfb.org/git/directfb/core/linux-fusion.git

 

二、編譯和安裝Fusion

# tar zxf linux-fusion-8.10.2.tar.gz
# cd linux-fusion-8.10.2
# make
# make install

注意:

1) Run 'make' and 'make install'. It builds and installs the module
   for the running kernel version as reported by 'uname -r'.
   If you want to build for another kernel, edit the Makefile.

2) Either run 'modprobe fusion' manually or add "fusion" to "/etc/modules".

3) Create the fusion device(s) if not using devfs or udev:

        mkdir /dev/fusion
        mknod /dev/fusion/0 c 250 0

        ...if you need more than one session

        mknod /dev/fusion/1 c 250 1
        mknod /dev/fusion/2 c 250 2

        ...and so on (currently limited to eight sessions)

4) Add udev rules to /etc/udev/rules.d/40-fusion.rules if using udev:

        KERNEL=="fusion[0-9]*", NAME="fusion/%n", GROUP="video", MODE="0660"

        ...customize to suit your needs

 

三、編譯和安裝DirectFB

# tar zxf DirectFB-1.6.1.tar.gz
# cd DirectFB-1.6.1
# ./configure --enable-x11 --enable-multi --enable-debug --enable-trace
# make
# make install

 

注意:爲使pkg-config可以找到DirectFB,須要添加尋索路徑:/usr/local/lib/,有兩種方式

 

方式1:

首先執行:vi /etc/ld.so.conf.d/qt-i386.conf ,並添加以下內容

/usr/local/lib/

而後執行:

ldconfig

 

方式2:

首先執行:

cd DirectFB-1.6.1
find . -name "*.pc"

而後將搜索出的「*.pc」文件copy至目錄/usr/lib/pkgconfig,執行:

image

 

四、編譯和安裝Example

# tar -zxvf DirectFB-examples-1.6.0.tar.gz
# cd DirectFB-examples-1.6.0
# ./configure --enable-debug
# make
# make install

 

 

3、運行示例

# depmod -a
# modprobe fusion # 安裝 fusion 內核模塊
# cd /usr/local/bin
# ./df_window

報錯以下:

~~~~~~~~~~~~~~~~~~~~~~~~~~| DirectFB 1.6.1 |~~~~~~~~~~~~~~~~~~~~~~~~~~
        (c) 2001-2012  The world wide DirectFB Open Source Community
        (c) 2000-2004  Convergence (integrated media) GmbH
      ----------------------------------------------------------------

(*) DirectFB/Core: Multi Application Core. (2015-03-22 11:51) [ DEBUG ][ TRACE ]
(*) Fusion/SHM: Using MADV_REMOVE (2.6.32.0 >= 2.6.19.2)
(*) Direct/Thread: Started 'Fusion Dispatch' (-1) [MESSAGING OTHER/OTHER 0/0] <10485760>...
(*) Direct/Thread: Started 'Fusion Deferred' (-1) [MESSAGING OTHER/OTHER 0/0] <10485760>...
(!) Direct/Util: Opening '/dev/fb0' failed!
    --> The requested operation or an argument is (currently) not supported
(-) [ 3047: -STACK- ]
  #0  0x00e431c3 in direct_try_open () from /usr/local/lib/libdirect-1.6.so.0 [0xe19000]
  #1  0x0044aa61 in dfb_fbdev_open () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #2  0x0044b2e7 in system_initialize () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #3  0x007a6178 in dfb_system_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #4  0x00737d5c in dfb_core_part_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #5  0x00734987 in dfb_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #6  0x006f41aa in DirectFB::ICore_Real::Initialize() () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #7  0x006f106e in CoreDFB_Initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #8  0x007351ea in dfb_core_arena_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #9  0x0072e303 in dfb_core_create () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #10 0x006efb00 in DirectFBCreate () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]

(!) DirectFB/FBDev: Error opening framebuffer device!
(-) [ 3047: -STACK- ]
  #0  0x0044aa61 in dfb_fbdev_open () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #1  0x0044b2e7 in system_initialize () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #2  0x007a6178 in dfb_system_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #3  0x00737d5c in dfb_core_part_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #4  0x00734987 in dfb_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #5  0x006f41aa in DirectFB::ICore_Real::Initialize() () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #6  0x006f106e in CoreDFB_Initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #7  0x007351ea in dfb_core_arena_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #8  0x0072e303 in dfb_core_create () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #9  0x006efb00 in DirectFBCreate () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]

(!) DirectFB/FBDev: Use 'fbdev' option or set FRAMEBUFFER environment variable.
(-) [ 3047: -STACK- ]
  #0  0x0044aa61 in dfb_fbdev_open () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #1  0x0044b2e7 in system_initialize () from /usr/local/lib/directfb-1.6-0/systems/libdirectfb_fbdev.so [0x447000]
  #2  0x007a6178 in dfb_system_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #3  0x00737d5c in dfb_core_part_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #4  0x00734987 in dfb_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #5  0x006f41aa in DirectFB::ICore_Real::Initialize() () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #6  0x006f106e in CoreDFB_Initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #7  0x007351ea in dfb_core_arena_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #8  0x0072e303 in dfb_core_create () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #9  0x006efb00 in DirectFBCreate () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]

(!) DirectFB/Core: Could not initialize 'system_core' core!
    --> A general initialization error occured
(-) [ 3047: -STACK- ]
  #0  0x00737d5c in dfb_core_part_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #1  0x00734987 in dfb_core_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #2  0x006f41aa in DirectFB::ICore_Real::Initialize() () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #3  0x006f106e in CoreDFB_Initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #4  0x007351ea in dfb_core_arena_initialize () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #5  0x0072e303 in dfb_core_create () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]
  #6  0x006efb00 in DirectFBCreate () from /usr/local/lib/libdirectfb-1.6.so.0 [0x6bd000]

df_window.c <136>:
    (#) DirectFBError [DirectFBCreate( &dfb )]: A general initialization error occured

解決方法:

執行:vi /etc/grub.conf

修改
kernel /vmlinuz-2.6.15-1.2054_FC5 ro root=LABEL=/ rhgb quiet

爲:
kernel /vmlinuz-2.6.15-1.2054_FC5 ro root=LABEL=/ rhgb quiet vga= 788

再次運行:

# ./df_window

效果以下

image

image

 

 

4、編程

程序1:在屏幕中間畫一條水平直線

//draw_line.c
#include <stdio.h>
#include <unistd.h>
#include <directfb.h>

//這是最上層的接口,全部的函數入口均由它(IDirectFB)而來
static IDirectFB *dfb = NULL;

//主平面,也就是「屏幕」了。在交互層使用DFSCL_FULLSCREEN,它是主層平面。
static IDirectFBSurface *primary = NULL;

//這裏存儲主平面的高和寬,從而爲其它的操做提供支持
static int screen_width = 0;
static int screen_height = 0;

//用以檢測錯誤的宏定義,用來檢測大部分的函數的返回是否正常。只適合用在小的測試程序
#define DFBCHECK(x...)                                               \
{                                                                    \
     DFBResult err = x;                                              \
     if (err != DFB_OK)                                              \
     {                                                               \
           fprintf( stderr, "%s <%d>:\n\t", __FILE__, __LINE__ );    \
           DirectFBErrorFatal( #x, err );                            \
     }                                                               \
}


int main (int argc, char **argv)
{
    //爲了建立一個平面,須要定義一個平面描述子(surface description)
    DFBSurfaceDescription dsc;

    //初始化
    DFBCHECK (DirectFBInit (&argc, &argv));
    DFBCHECK (DirectFBCreate (&dfb));
    DFBCHECK (dfb->SetCooperativeLevel (dfb, DFSCL_FULLSCREEN));

    //設定dsc的一些屬性,如今能夠不用關心
    dsc.flags = DSDESC_CAPS;
    dsc.caps = DSCAPS_PRIMARY | DSCAPS_FLIPPING;

    //使用咱們設定的dsc建立主平面(primary)
    DFBCHECK (dfb->CreateSurface( dfb, &dsc, &primary ));

    //獲得主平面的寬與高
    DFBCHECK (primary->GetSize (primary, &screen_width, &screen_height));

    //經過畫一個和主平面同等大小的矩形來清空主平面;默認顏色爲黑色
    DFBCHECK (primary->FillRectangle (primary, 0, 0, screen_width, screen_height));

    //爲了能顯示畫出來的線,先設置一下線的顏色,線的位置在屏幕的中間
    DFBCHECK (primary->SetColor (primary, 0x80, 0x80, 0xff, 0xff));
    DFBCHECK (primary->DrawLine (primary,0, screen_height / 2,screen_width - 1, screen_height / 2));

    //顯示
    DFBCHECK (primary->Flip (primary, NULL, 0));

    //等待5秒後,程序自動退出
    sleep(5);
    primary->Release( primary );
    dfb->Release( dfb );
    return 23;
}

編譯

gcc -L/usr/local/lib -I/usr/local/include/directfb -ldirectfb -lpthread draw_line.c -o draw_line

運行

image

 

程序2:移動鼠標繪製線條

#include <stdio.h>
#include <directfb/directfb.h>

static IDirectFB                *dfb = NULL;
static IDirectFBDisplayLayer    *layer = NULL;
static IDirectFBSurface            *surface = NULL;
static IDirectFBEventBuffer        *events = NULL;

static int screen_width = 0;
static int screen_height = 0;

int main(int argc, char *argv[])
{
    int pitch;
    int i,j;
    int quit = 0;
    int x = 0, y = 0, old_x = 0, old_y = 0;

    DirectFBInit(&argc, &argv);
    DirectFBCreate(&dfb);

    dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer);
    dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_TRUE, &events);
    layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE);
    layer->GetSurface(layer, &surface);

    surface->GetSize(surface, &screen_width, &screen_height);

    surface->SetColor(surface, 0, 56, 0 , 0xff);
    surface->FillRectangle(surface, 0, 0, screen_width, screen_height);

    while(!quit)
    {
        DFBEvent    evt;
        events->WaitForEvent(events);    //等待事件

        while(!quit && events->GetEvent(events, &evt) == DFB_OK)
        {
            if(evt.clazz == DFEC_INPUT)
            {
                switch(evt.input.type)
                {
                case    DIET_KEYPRESS: //響應鍵盤事件
                    if(evt.input.key_symbol == DIKS_ESCAPE) //退出程序
                    {
                        quit = 1;
                    }
                    else     if(evt.input.key_symbol == DIKS_SMALL_C)  //清屏
                    {
                        surface->SetColor(surface, 0, 56, 0 , 0xff);//設置當前色
                        surface->FillRectangle(surface, 0, 0, screen_width, screen_height);
                    }
                    break;

                case    DIET_AXISMOTION:
                    if(evt.input.flags & DIEF_AXISREL)
                    {
                        switch(evt.input.axis)
                        {
                        case DIAI_X:
                            x+=evt.input.axisrel;
                            break;
                        case DIAI_Y:
                            y+=evt.input.axisrel;
                            break;
                        default :
                            break;
                        }//switch(evt.input.axis){

                        if(x < 0)
                        {
                            x = 0;
                        }
                        if(y < 0)
                        {
                            y = 0;
                        }
                    }

                    surface->SetColor(surface, 0, 0xff, 0, 0xff);
                    surface->DrawLine(surface, old_x, old_y, x, y);
                    old_x = x;
                    old_y = y;
                    break;

                default:
                    break;
                }//switch
            }//if
        }//while(!quit && events->GetEvent(events, &evt) == DFB_OK){
    }//while(!quit){

    surface->Release(surface);
    layer->Release(layer);
    dfb->Release(dfb);

    return 0;
}

編譯

gcc -L/usr/local/lib -I/usr/local/include/directfb -ldirectfb -lpthread DynamicLine.c -o DynamicLine

運行

image

相關文章
相關標籤/搜索