Thiết kế giao diện đồ họa cho Linux với Gnome 2

Đặng Trần Hiếu
(lt2hieu2004)

Thành viên danh dự
1. Giới thiệu chung:
Loạt bài thiết kế giao diện đồ họa cho Linux với Gnome 2 được viết dành cho:
- Những người biết C, hiểu được các tính năng nâng cao như con trỏ, con trỏ đến con trỏ, quản lý bộ nhớ động,...
- Có kiến thức tốt về C macro & các chỉ thị tiền xử lý C (C preprocessors).
- Biết cách sử dụng các chương trình cơ bản của *nix (chỉ cần biết cách khởi động, sử dụng trình soạn thảo,...)
- Kiến thức cơ bản về *nix (processes, libraries,...)
- Không cần có kiến thức về C++​
2. Các kiểu cơ bản của GLib
Trước tiên, để có thể thiết kế giao diện đồ họa cho Linux dùng Gnome 2, phải có kiến thức cơ bản về GLib (libglib-2.0). Để sử dụng các tính năng của glib, phải include header glib.h như sau:
Mã:
#include <glib.h>
Các kiểu cơ bản của GLib gồm có:
  • gchar tương đương với char của C
  • guchar tương đương với unsigned char của C
  • gint tương đương với int của C
  • guint tương đương với unsigned int của C
  • gshort tương đương với short của C
  • gushort tương đương với unsigned short của C
  • glong tương đương với long của C
  • gulong tương đương với unsigned long của C
  • gfloat tương đương với float của C
  • gdouble tương đương với double của C
  • gint8 tương đương với int 8 bits của C
  • guint8 tương đương với unsigned int 8 bits của C
  • gint16 tương đương với int 16 bits của C
  • guint16 tương đương với unsigned int 16 bits của C
  • gint32 tương đương với int 32 bits của C
  • guint32 tương đương với unsigned int 32 bits của C
  • gint64 tương đương với int 64 bits của C
  • guint64 tương đương với unsigned int 64 bits của C
  • gpointer tương đương với void * của C
  • gconstpointer tương đương với const void * của C
  • gboolean là 1 kiểu mới trong GLib (ko có trong C, tương đương với boolean của C++), nhận 2 giá trị TRUE hoặc FALSE.
Trên đây chỉ là những kiểu thường dùng nhất, những kiểu ít được dùng hơn sẽ được giải thích khi cần đến.

Lý do GLib cần có kiểu riêng mà ko sử dụng các kiểu cơ bản của C là vì các kiểu cơ bản của C có thể có độ rộng khác nhau trên các loại máy khác nhau. Ví dụ như kiểu double ở i686 có độ rộng 32 bits nhưng ở máy Alpha lại có độ rộng 64 bits, dùng kiểu cơ bản của GLib giúp mã nguồn có khả năng tương thích tối đa với nhiều hệ máy khác nhau.

3. Quản lý bộ nhớ động với GLib:
Thay vì sử dụng malloc(), realloc()free() của C, GLib sử dụng các hàm g_malloc(), g_realloc, g_malloc0g_free.

Ưu điểm của các hàm quản lý bộ nhớ động của GLib là nó có tính năng kiểm soát lỗi thay vì bị core dump như trong C.

Nhìn chung, cú pháp của các hàm này là giống với các hàm cơ bản của C:
  • gpointer g_malloc(gulong n) tương đương với void *malloc(size_t size)
  • gpointer g_malloc0(gulong n) - giống như g_malloc nhưng cấp phát bộ nhớ giống như calloc tức là đặt giá trị của bộ nhớ vừa cấp phát bằng 0.
  • gpointer g_try_malloc(gulong n) - giống như g_malloc nhưng không có tính năng kiểm soát lỗi.
  • gpointer g_realloc(gpointer mem, gulong n) tương đương với void *realloc(void *ptr, size_t size)
  • gpointer g_try_realloc(gpointer mem, gulong n) - tự hỉu b-)
  • void g_free(gpointer mem) tương đương với void free(void *ptr)

Ngoài các hàm trên, GLib còn có các hàm g_new, g_new0, g_renew. Các hàm này có thể thay thế g_malloc, g_malloc0g_realloc, trên thực tế, nên sử dụng các hàm này khi nào có thể vì các hàm này giúp viết mã nhanh, ít lỗi hơn & dễ hiểu hơn. Ví dụ:
Mã:
#include <glib.h>

int main(void)
{
    gchar *a;

    /* Sử dụng g_malloc để cấp phát bộ nhớ cho 1 chuỗi 30 ký tự */
    a = (gchar *)g_malloc(sizeof(gchar)*30);

    /* Dùng g_new */
    a = g_new(gchar, 30);

    /* Sử dụng g_malloc0 để cấp phát bộ nhớ cho 1 chuỗi 30 ký tự */
    a = (gchar *)g_malloc0(sizeof(gchar)*30);

    /* Sử dụng g_new0 */
    a = g_new0(gchar, 30);

    /* Mở rộng chuỗi 30 ký tự đã cấp phát bộ nhớ thành 40 ký tự */
    a = (gchar *)g_realloc(a, sizeof(gchar)*40);

    /* Sử dụng g_renew */
    a = g_renew(a, 40);

    return 0;
}
Kết thúc phần 1. Phần 2 sẽ được viết tiếp ngày mai (hoặc hôm nay). /:)
 
Chỉnh sửa lần cuối:
Thiết kế giao diện đồ họa cho Linux với Gnome 2 (P. 2)

Xử lý chuỗi C với GLib

1. Một số hằng cần chú ý:
Định nghĩa 1 số hằng cần chú ý:
Mã:
#define G_ASCII_DTOSTR_BUF_SIZE (29 + 10)
#define G_STR_DELIMITERS "_-|<>."

typedef unsigned long size_t;
typedef long ssize_t;
typedef size_t gsize;
typedef ssize_t gssize;

2. Các hàm xử lý chuỗi C của GLib:
GLib cung cấp 1 số hàm có thể sử dụng trên chuỗi của C (không phải kiểu chuỗi GString của GLib) bao gồm:
- gchar *g_strdup(const gchar *source) tương đương với char *strdup(const char *source) dùng để copy chuỗi source và trả lại con trỏ đến chuỗi mới.

- gchar *g_strndup(const gchar *source, gsize n) - như trên, nhưng chỉ copy n ký tự đầu tiên của chuỗi source, nếu n > strlen(source) thì g_strdup & g_strndup là như nhau. Bất kể độ dài của chuỗi kết quả, chuỗi luôn bao gồm thêm 1 ký tự ở cuối (ký tự kết thúc chuỗi NULL hay \0). Chính vì vậy khi cấp phát bộ nhớ cho chuỗi kết quả luôn phải cấp phát bộ nhớ bằng n + 1.

- gchar *strnfill(gsize length, gchar *fill_char) - hàm này chỉ có trong GLib, mục đích là tạo 1 chuỗi có độ dài length và đặt mỗi ký tự của chuỗi thành fill_char.

- gchar *g_strdup_printf(const gchar *format, ...) tương đương với int sprintf(char *buf, const char *format, ...) chỉ có điều ko phải chỉ định buffer vì g_strdup_printf sẽ tự động cấp phát bộ nhớ cho buffer và trả lại buffer.

- gchar *g_strdup_vprintf(const gchar *format, va_list args) tương tự như g_strdup_printf nhưng nhận đối số như vsprintf.

- gchar *g_strescape(const gchar *source, const gchar *exceptions), chuyển đổi tất cả các ký tự ko phải là ASCII hoặc các ký tự có mã ASCII nằm dưới ký tự space bar. Ví dụ ký tự backspace sẽ thành \b, ký tự xuống dòng thành \n, ký tự \ thành \\. Những ký tự nằm trong chuỗi exceptions sẽ ko được chuyển đổi.

- gchar *g_strcompress(const gchar *source) đảo ngược lại hành vi của g_strescape.

- gchar *g_strconcat(const gchar *str, ..., NULL) - nối tất cả các chuỗi đối số lại với nhau (cần có NULL là đối số cuối cùng).

- gchar *g_strjoin(const gchar *seperator, ..., NULL) - nối tất cả các chuỗi đối số lại với nhau với seperator ở giữa (cần có NULL là đối số cuối cùng).

- gchar *g_stpcpy(gchar *dest, const gchar *source) tương đương với char *strcpy(char *dest, const char *source). Cần chú ý, tên hàm là g_stpcpy chứ ko phải là g_strcpy.

- gint g_snprintf(gchar *buf, gulong n, const gchar *format, ...) tương đương với int snprintf(char *buf, size_t n, const char *format, ...) Giá trị trả lại là số ký tự sẽ được ghi nếu buffer đủ lớn để chứa hết, kết quả trả về của g_snprintf theo chuẩn ISO C99, trái với chuẩn C gốc sẽ trả lại -1 nếu buffer không đủ lớn và trong trường hợp này chuẩn C gốc sẽ khiến buffer không được kết thúc bởi NULL.

- gint g_vsnprintf(gchar *buf, gulong n, va_list args) giống như trên nhưng nhận va_list làm đối số.

- gchar *g_strreverse(gchar *str) đảo ngược thứ tự chữ cái của chuỗi str.

- gchar *g_strchug(gchar *str) xóa các ký tự khoảng trắng ở đầu chuỗi str (space bar, tab,...).

- gchar *g_strchomp(gchar *str) xóa các ký tự khoảng trắng ở cuối str.

- gchar *g_strstrip(gchar *str) kết hợp cả 2 hàm trên.

- gchar *g_strdelimit(gchar *str, const gchar *search4, gchar *replacewith) tìm trong chuỗi str tất cả các ký tự trong chuỗi search 4 và thay các ký tự này bằng replacewith. Nếu search4 là NULL, search4 sẽ có giá trị G_STR_DELIMITERS.

- gchar *g_strcanon(gchar *str, const gchar *valid, gchar *replacewith) tìm trong chuỗi str tất cả các ký tự không nằm trong chuỗi valid, thay thế các ký tự này với replacewith.

- gchar *g_strstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle) tìm trong haystack_len ký tự đầu tiên của haystack chuỗi needle, trả lại con trỏ đến vị trí của chuỗi needle đầu tiên tìm được. Nếu không tìm được needle, hàm trả lại NULL. Vị trí của chuỗi needle đầu tiên tìm được được xác định bằng cách lấy kết quả của hàm trừ đi haystack (Chú ý: ký tự đầu tiên của chuỗi là 0).

- gchar *g_strrstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle) tương tự như hàm trên nhưng tìm lần xuất hiện cuối của needle.

- gsize g_printf_string_upper_bound(const gchar *format, va_list args) tính độ dài tối đa để chứa chuỗi kết quả của vsprintf với các đối số đã cho.

- gdouble g_ascii_strtod(const char *nptr, const char **endptr) chuyển đổi chuỗi nptr thành 1 số kiểu gdouble. Nếu endptr không phải là NULL, g_ascii_strtod sẽ trả về endptr ký tự ngay sau ký tự cuối cùng được dùng để chuyển đổi.

- gchar *g_ascii_dtostr(gchar *buf, gint buf_len, gdouble d) lấy số d và chuyển đổi số này thành chuỗi, ghi chuỗi này vào buf tối đa buf_len ký tự, hàm trả về con trỏ đến chuỗi buf. Chuỗi kết quả không bao giờ lớn hơn G_ASCII_DTOSTR_BUF_SIZE.

- gchar **g_strsplit(const gchar *str, const gchar *delimiter, gint max_tokens) sử dụng delimiter để chia chuỗi str thành 1 mảng nhiều chuỗi với tối đa max_tokens thành viên. Trả về mảng.

- gchar *g_str_joinv(const gchar *seperator, gchar **str_array) nối các chuỗi trong str_array thành 1 chuỗi ngăn cách bởi separator.

- gchar **g_strdupv(gchar **str_array) copy str_array & từng thành viên của nó & trả về con mảng đã copy.

- void g_strfreev(gchar **str_array) giải phóng bộ nhớ được cấp phát cho str_array. (Chú ý: ko được dùng bất cứ hàm nào ngoài g_strfreev để giải phóng bộ nhớ của mảng chuỗi là kết quả của g_strsplit hay g_strdupv.​
Kết thúc phần 2.
 
Chỉnh sửa lần cuối:
Back
Bên trên