使用XCB編寫X Window程式(05):使用非同步方式和X Server通訊及擷取和設定視窗的屬性

來源:互聯網
上載者:User

標籤:style   blog   color   使用   os   資料   

  在前面的例子中,我們從來沒有關心過調用XCB函數時,該函數和X Server的通訊是同步的還是非同步。因為在前面的例子中,我們基本上不關心XCB函數的傳回值,只有在上一篇中,由於某些操作需要關心它們是否成功(比如是否成功開啟字型、是否成功建立GC等),才涉及到XCB函數的傳回值。

  在這一篇中會更進一步,因為是擷取視窗的屬性,所以肯定要關注從X Server擷取的資料,這時,將會涉及到XCB函數同X Server的通訊是同步的還是非同步。什麼是同步?就是說調用一個函數向X Server發出一個請求後,該函數要一直等到X Server回複的資料函數才返回。什麼是非同步?就是說調用一個XCB函數向X Server發出一個請求後,不用等X Server回複的資料,該函數直接返回。傳統的Xlib使用的是同步的方式,而XCB則是使用的非同步方式。很顯然,使用非同步方式可以獲得更好的效率。這也是XCB取代Xlib的一個主要原因。

  那麼既然XCB使用的是非同步方式,調用一個XCB函數後沒有等到X Server的回複該函數就已經返回了,那麼我們真的需要X Server回複的資料怎麼辦呢?對於這個問題,在XCB中使用的是這樣一種解決方式:XCB函數先返回一個cookie,資料轉送在後台進行,由XCB自動處理,當需要使用從X Server擷取的資料時,再調用另外一個函數通過該cookie擷取資料。後面將會看到具體的樣本。

 

操控視窗的屬性

  前面展示了怎麼建立視窗以及怎麼在視窗中畫圖。其實對於視窗,還可以有更多的操作。可以認為這些操作都是通過擷取和設定視窗的屬性來進行。視窗可以有很多的屬性,而且這些屬性的設定方式還不一樣。下面來回顧一下建立視窗的函數:

xcb_void_cookie_t xcb_create_window (    xcb_connection_t *connection,    /* Pointer to the xcb_connection_t structure */    uint8_t           depth,         /* Depth of the screen */    xcb_window_t      wid,           /* Id of the window */    xcb_window_t      parent,        /* Id of an existing window that should be the parent of the new window */    int16_t           x,             /* X position of the top-left corner of the window (in pixels) */    int16_t           y,             /* Y position of the top-left corner of the window (in pixels) */    uint16_t          width,         /* Width of the window (in pixels) */    uint16_t          height,        /* Height of the window (in pixels) */    uint16_t          border_width,  /* Width of the window‘s border (in pixels) */    uint16_t          _class,    xcb_visualid_t    visual,    uint32_t          value_mask,    const uint32_t   *value_list );

  可以看到:1、有一部分屬性是直接通過xcb_create_window的參數設定的,比如視窗的色彩深度、位置、大小、邊框寬度等;2、一部分視窗的屬性通過mask、values的方式設定,比如前景色彩、背景色、需要關注的事件等;3、還有一部分屬性需要通過另外一種方式來設定,這種方式我之前沒有展示過,那就是通過xcb_change_property函數來設定,這些屬性比較特別,每一個都稱之為atom。從xcb_create_window的函數簽名還可以看出,xcb_create_window確實是返回一個xcb_void_cookie_t類型的值,也就是返回一個cookie。這驗證了我之前關於XCB函數是非同步這個說法。

  xcb_change_property的函數簽名如下:

xcb_void_cookie_t xcb_change_property (    xcb_connection_t *c,       /* Connection to the X server */    uint8_t          mode,     /* Property mode */    xcb_window_t     window,   /* Window */    xcb_atom_t       property, /* Property to change */    xcb_atom_t       type,     /* Type of the property */    uint8_t          format,   /* Format of the property (8, 16, 32) */    uint32_t         data_len, /* Length of the data parameter */    const void       *data     /* Data */); 

  可以看出,該函數要同時關注atom的id、atom的類型及atom的值,還要關注該值的格式和長度。也就是說,每一個atom由一個id標識,它可以有自己的類型和值。比如一個atom可能是一個數字,而另一atom可能是一個字串。該函數的mode參數可以取下面值中的一個:

XCB_PROP_MODE_REPLACEXCB_PROP_MODE_PREPENDXCB_PROP_MODE_APPEND

 

  使用atom來表示視窗的屬性主要是為了和別的程式之間進行溝通,比如和視窗管理器溝通。下面是一個設定視窗標題以及視窗最小化後工作列標題的樣本:

    #include <string.h>    #include <xcb/xcb.h>    #include <xcb/xcb_atom.h>    int    main ()    {        /* open the connection to the X server */        xcb_connection_t *connection = xcb_connect (NULL, NULL);        /* get the first screen */        xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;        /* create the window */        xcb_window_t window = xcb_generate_id (connection);        xcb_create_window (connection,                            0,                             /* depth               */                           window,                           screen->root,                  /* parent window       */                           0, 0,                          /* x, y                */                           250, 150,                      /* width, height       */                           10,                            /* border_width        */                           XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */                           screen->root_visual,           /* visual              */                           0, NULL );                     /* masks, not used     */        /* set the title of the window */        char *title = "Hello World !";        xcb_change_property (connection,                             XCB_PROP_MODE_REPLACE,                             window,                             XCB_ATOM_WM_NAME,                             XCB_ATOM_STRING,                             8,                             strlen (title),                             title );        /* set the title of the window icon */        char *iconTitle = "Hello World ! (iconified)";        xcb_change_property (connection,                             XCB_PROP_MODE_REPLACE,                             window,                             XCB_ATOM_WM_ICON_NAME,                             XCB_ATOM_STRING,                             8,                             strlen(iconTitle),                             iconTitle);        /* map the window on the screen */        xcb_map_window (connection, window);        xcb_flush (connection);        /* event loop (in this case, no events to handle) */         while (1) {}        return 0;    }

  如果想更改視窗的大小、位置、邊框寬度等屬性的話,可以通過如下函數進行:

xcb_void_cookie_t xcb_configure_window (    xcb_connection_t *c,            /* The connection to the X server*/    xcb_window_t      window,       /* The window to configure */    uint16_t          value_mask,   /* The mask */    const uint32_t   *value_list    /* The values to set */);

  可以看到,該函數再次用到了mask、valuelist這種模式。該函數可操作的屬性有如下這些:

    XCB_CONFIG_WINDOW_X             // new x coordinate of the window‘s top left corner    XCB_CONFIG_WINDOW_Y             // new y coordinate of the window‘s top left corner    XCB_CONFIG_WINDOW_WIDTH         // new width of the window    XCB_CONFIG_WINDOW_HEIGHT        // new height of the window    XCB_CONFIG_WINDOW_BORDER_WIDTH  // new width of the border of the window    XCB_CONFIG_WINDOW_SIBLING    XCB_CONFIG_WINDOW_STACK_MODE    // the new stacking order

  寫了半天也還沒寫到XCB函數和X Server非同步通訊的用法。為了不把這篇隨筆寫得又臭又長,xcb_configure_window具體怎麼用我就不舉例了。大家可以自行閱讀XCB的官方教程。下面來看什麼是非同步。

 

擷取視窗的屬性

  有很多函數可以擷取視窗的屬性,比如最常見的 xcb_get_geometry 和 xcb_get_geometry_reply ,看到geometry肯定會想到長寬高、橫座標縱座標什麼的。確實,這兩個函數就是用來擷取視窗的位置、大小這樣一些資訊的。而且這兩個函數總是成對出現。因為xcb_get_geometry被調用後,它是馬上返回的,這時X Server的資料還沒傳回來呢,所以,等資料傳回來了,還得調用xcb_get_geometry_reply把這些資料讀出來。這兩個函數的簽名如下:

xcb_get_geometry_cookie_t xcb_get_geometry (    xcb_connection_t *connection,    xcb_drawable_t    drawable );xcb_get_geometry_reply_t *xcb_get_geometry_reply (    xcb_connection_t          *connection,    xcb_get_geometry_cookie_t  cookie,    xcb_generic_error_t      **error);

  可以看到,第一個函數返回一個類型為xcb_get_geometry_cookie_t的cookie,第二個函數再以該cookie為參數,返回具體的geometry資訊,這就是非同步。該資訊是一個類型為xcb_get_geometry_reply_t的指標,該類型定義如下:

typedef struct {        uint8_t      response_type;        uint8_t      depth;         /* depth of the window */        uint16_t     sequence;        uint32_t     length;        xcb_window_t root;          /* Id of the root window *>        int16_t      x;             /* X coordinate of the window‘s location */        int16_t      y;             /* Y coordinate of the window‘s location */        uint16_t     width;         /* Width of the window */        uint16_t     height;        /* Height of the window */        uint16_t     border_width;  /* Width of the window‘s border */    } xcb_get_geometry_reply_t;

  前面說過,視窗有很多屬性。geometry屬性指示視窗屬性的一部分,所以,還有一對更猛的擷取視窗屬性的函數及其使用到的資料結構,如下:

typedef struct {        uint8_t        response_type;        uint8_t        backing_store;        uint16_t       sequence;        uint32_t       length;        xcb_visualid_t visual;                /* Visual of the window */        uint16_t       _class;        uint8_t        bit_gravity;        uint8_t        win_gravity;        uint32_t       backing_planes;        uint32_t       backing_pixel;        uint8_t        save_under;        uint8_t        map_is_installed;        uint8_t        map_state;             /* Map state of the window */        uint8_t        override_redirect;        xcb_colormap_t colormap;              /* Colormap of the window */        uint32_t       all_event_masks;        uint32_t       your_event_mask;        uint16_t       do_not_propagate_mask;} xcb_get_window_attributes_reply_t;xcb_get_window_attributes_cookie_t xcb_get_window_attributes (    xcb_connection_t *connection,    xcb_window_t      window );xcb_get_window_attributes_reply_t *xcb_get_window_attributes_reply (    xcb_connection_t                   *connection,    xcb_get_window_attributes_cookie_t  cookie,    xcb_generic_error_t               **e );

  這些都還是基本的。編寫簡單的視窗程序,瞭解這些基本的方法就夠了。但是GUI編程,總還有一些高深的需求,比如枚舉系統中所有的視窗。我這裡用到了“枚舉”這個詞,是因為Win32 API用了這個詞。而在X Window中,這樣的操作叫做“遍曆”。很顯然,在GUI系統中,所有的視窗都是通過parent、child這樣的方式呈樹形組織起來的,所以通過“遍曆”視窗樹來“枚舉”系統中的所有視窗,其實也不難。這個任務需要用到這樣的函數和資料結構:

typedef struct {        uint8_t      response_type;        uint8_t      pad0;        uint16_t     sequence;        uint32_t     length;        xcb_window_t root;        xcb_window_t parent;       /* Id of the parent window */        uint16_t     children_len;        uint8_t      pad1[14];} xcb_query_tree_reply_t;xcb_query_tree_cookie_t xcb_query_tree (    xcb_connection_t        *connection,    xcb_window_t             window );xcb_query_tree_reply_t *xcb_query_tree_reply (    xcb_connection_t        *connection,    xcb_query_tree_cookie_t  cookie,    xcb_generic_error_t    **error );

  不過該函數好像只能從子視窗尋找父視窗,很顯然要完成遍曆,還必須要能從父視窗尋找子視窗。X Window中有通過父視窗尋找子視窗的方法嗎?大家慢慢研究去吧。

(京山遊俠於2014-07-21發佈於部落格園,轉載請註明出處。)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.