函数指针、回调函数与 GObject 闭包

本文首先复习一下基于 C 语言函数指针和回调函数的概念,进而学习 GObject 闭包的用法。这些知识都与面向对象程序设计基本上没有什么关系。

函数指针
所谓函数指针,就是可以指向函数的指针,例如:

int foo(void)
{
        return 1;
}
 
int main(void)
{       
        int (*func) (void);
 
        func = foo;
        func ();
 
        return 0;
}

代码中的 func 即为一个函数指针,它可以指向一个无参数且返回值为整型数的函数,还可以调用它。

只要不会将:

int (*func) (void);

int *func (void);

弄混(后者是返回值类型为整型数指针的函数),那么对于函数指针的理解就没什么大问题了。

由于 “int (*func) (void)” 这种声明函数指针的形式看上去有点怪异,所以很多人喜欢用 typedef 将函数值指针定义成类型以便使用,例如:

typedef int (*Func) (void);
 
Func func = foo;

如果对于上述内容理解起来并不费劲,那么下面就可以谈谈回调函数了。

回调函数
在编写一些程序库的时候,设计者有时会认为有些功能,不应该由他自作主张,而应当由使用者来决定。这方面,比较有名的例子是 C 标准库中的 qsort 函数:

void qsort (void *base,
            size_t nmemb,
            size_t size,
            int (*compar) (const void *, const void *));

它的第 4 个参数即为函数指针,它可以指向形式为:

int foo (const void *param1, const void *param2);

的所有函数。

Q:这个函数指针的作用是什么?
A:它是用来指向用户提供的回调函数的。

Q:用户提供的回调函数是什么意思?
A:因为 qsort 的设计目标是对任意类型的同类数据集合进行快速排序,比如一组整型数的排序,或者一组字符串的排序。既然是排序,那么 qsort 必须知道如何判定具体类型的两个数据的大小,而 qsort 的设计者认为这一功能应当交给用户来实现,因为他并不清楚用户究竟要使用 qsort 对哪种类型的数据集合进行排序,只有用户自己清楚。

Q:用户提供的回调函数究竟是什么意思?
A:就是对于用户所要排序的数据集合而言,用户提供一个可以比较该数据集合中任意两个元素大小的函数,这个函数便是 qsort 的回调函数。

用户、被调用的函数 qsort 以及回调函数,它们之间的关系如下图所示:

下面是使用 qsort 函数进行字符串数组递增排序的示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
static int str_compare(const void *s1, const void *s2)
{
        char *str1 = *(char **)s1;
        char *str2 = *(char **)s2;      
        size_t l1 = strlen (str1);
        size_t l2 = strlen (str2);
 
        if (l1 > l2)
                return 1;
        else if (l1 == l2)
                return 0;
        else
                return -1;
}
 
int main(void)
{
        char *str_array[5] = {"a", "abcd", "abc", "ab", "abcde"};
        qsort (&str_array, 5, sizeof (char *), str_compare);
 
        for (int i = 0; i< 5; i++)
                printf ("%s ", str_array[i]);
        printf ("\n");
 
        return 0;
}

闭包(Closure)的概念
从上一节,我们通过函数指针向 qsort 函数传入了一个函数 str_compare,这个函数被称为回调函数,但是它还有一个比较深奥的名字——“闭包”。

所谓闭包,简而言之,就是一个函数加上它所访问的所有非局部变量,而所谓“非局部变量”,表示这些变量对于那个函数而言既非局部变量,也非全局变量。

我们向 qsort 传入函数 str_compare,它所接受排序数据集合中的 2 个元素,而且 2 个元素对于 str_compare 而言,既非是全局变量,也非其局部变量,因此 str_compare 与这 2 个参数形成了一个闭包。

在许多动态语言中,闭包通常也被昵称为“函数是第一类对象”,即函数与那些语言中基本类型具有相同的权利,例如函数可以存储在变量中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。

恶魔来临
在 C 语言中,利用函数指针并配合参数的复制与传递,可模拟闭包这种结构,但是在可读性上没有那些内建支持闭包的语言优雅。

GObject 提供了 GClosure 对象与方法,实现了功能比较全面的 C 闭包模拟,我们可以在程序中直接使用它。下面,通过一个很小的示例,演示 GClosure 的使用。

先来看一个非 GClosure 的 C 闭包示例:

#include <stdio.h>
#include <math.h>
#include <string.h>
 
typedef int (*Func) (void *, void *);
 
static void compare(void *a, void *b, Func callback)
{
        int r = callback (a, b);
 
        if (r == -1)
                printf ("a < b\n");
        else if (r == 0)
                printf ("a = b\n");
        else
                printf ("a > b\n");
}
 
static int float_compare(void *a, void *b)
{
        float *f1 = (float *)a;
        float *f2 = (float *)b;
 
        if (*f1 > *f2)
                return 1;
        else if (fabs (*f1 - *f2) <= 10E-6)
                return 0;
        else
                return -1;
}
 
static int str_compare(void *a, void *b)
{
        size_t len1 = strlen ((char *)a);
        size_t len2 = strlen ((char *)b);
 
        if (len1 > len2)
                return 1;
        else if (len1 == len2)
                return 0;
        else
                return -1;
}
 
int main(void)
{      
        float a = 123.567;
        float b = 222.222;
        Func func = float_compare;
        compare (&a, &b, func);
 
        char *s1 = "hello world!";
        char *s2 = "hello!";
        func = str_compare;
        compare (s1, s2, func);
 
        return 0;
}

上述代码主要实现了一个 compare 函数,它可以比较两个任意类型数据的大小,前提是你要向它提供特定的回调函数(闭包),例如代码中的 float_compare 与 str_compare 函数,它们分别实现了浮点数比较与字符串比较。

将上述程序改为 GClosure 实现,如下:

#include <math.h>
#include <glib-object.h>
 
void g_cclosure_user_marshal_INT__VOID_VOID(GClosure     *closure,
                                        GValue       *return_value G_GNUC_UNUSED,
                                        guint         n_param_values,
                                        const GValue *param_values,
                                        gpointer      invocation_hint G_GNUC_UNUSED,
                                        gpointer      marshal_data)
{
        typedef gint (*GMarshalFunc_INT__VOID_VOID) (gpointer     data1,
                                                     gpointer     data2);
        register GMarshalFunc_INT__VOID_VOID callback;
        register GCClosure *cc = (GCClosure*) closure;
        register gpointer data1, data2;
        gint v_return;
 
        g_return_if_fail (return_value != NULL);
        g_return_if_fail (n_param_values == 1);
 
        if (G_CCLOSURE_SWAP_DATA (closure))
        {
                data1 = closure->data;
                data2 = g_value_peek_pointer (param_values + 0);
        }
        else
        {
                data1 = g_value_peek_pointer (param_values + 0);
                data2 = closure->data;
        }
        callback = (GMarshalFunc_INT__VOID_VOID) (
                marshal_data ? marshal_data : cc->callback);
 
        v_return = callback (data1, data2);
 
        g_value_set_int (return_value, v_return);
}
 
static void compare(GClosure *closure, void *b)
{
        GValue return_value = {0};
        GValue param_value  = {0};
        g_value_init (&return_value, G_TYPE_INT);
        g_value_init (&param_value, G_TYPE_POINTER);
 
        g_value_set_pointer (&param_value, b);
 
        g_closure_invoke (closure, &return_value, 1, &param_value, NULL);
        gint r = g_value_get_int (&return_value);
 
        if (r == -1)
                g_print ("a < b\n");
        else if (r == 0)
                g_print ("a = b\n");
        else
                g_print ("a > b\n");
 
        g_value_unset (&return_value);
        g_value_unset (&param_value);
}
 
static gint float_compare(void *a, void *b)
{
        gfloat *f1 = a;
        gfloat *f2 = b;
 
        if (*f1 > *f2)
                return 1;
        else if (fabs (*f1 - *f2) <= 10E-6)
                return 0;
        else
                return -1;
}
 
static gint str_compare(void *a, void *b)
{
        size_t len1 = g_utf8_strlen ((gchar *)a, -1);
        size_t len2 = g_utf8_strlen ((gchar *)b, -1);
 
        if (len1 > len2)
                return 1;
        else if (len1 == len2)
                return 0;
        else
                return -1;
}
 
int main(void)
{
        g_type_init ();
 
        gfloat a = 123.567;
        gfloat b = 222.222;
        GClosure *closure =
                g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, &b);
        g_closure_unref (closure);
 
        gchar *s1 = "Hello World!\n";
        gchar *s2 = "Hello!\n";
        closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);
        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, s2);
        g_closure_unref (closure);
 
        return 0;
}

很恐怖,看上去 GClosure 并没有简化闭包的实现,反而将其更加复杂化了,比如多出了 GClosure、GValue 等数据类型,还多出来一个 my_cclosure_user_marshal_INT__VOID_VOID 函数,而且 compare 函数的第 1 个参数的类型也变成 GClosure 指针类型了。

理解 GClosure
对于上一节所给出的 GClosure 的应用示例,我们从 main 函数入手解读一下。

首先看下面的代码:

/* main 函数中的代码 */
        gfloat a = 123.567;
        gfloat b = 222.222;
        GClosure *closure =
                g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, &b);
        g_closure_unref (closure);

g_cclosure_new 函数创建了一个面向 C 语言的闭包。之所以在此强调是面向 C 语言,那是因为 GObject 的闭包是面向任意语言的,例如 pyg_closure_new 函数可以创建 python 语言的闭包。

g_cclosure_new 函数所创建的 C 语言闭包结构体是 GCClosure,其定义如下:

typedef _GCClosure GCClosure;
struct _GCClosure {
        GClosure    closure;
        gpointer    callback;
};

联想一下 GObject 的子类化,上述代码暗示着 GCClosure 是 GClosure 的子类,不过这里没有类结构体以及实例化等概念,仅仅是结构体的继承。

在阅读下面的内容之前,一定要注意分辨 GCClosure 与 GClosure,它们的名字太相似了

GCClosure 结构体包含了一个 GClosure 结构体 closure 以及一个无类型的数据指针 callback(因为 gpointer 是 void * 的别名)。前者是 GObject 提供的闭包结构,后者是一个指向回调函数的指针。这里一定要注意,用无类型指针指向一个函数,是 GObject 设计的一个缺陷,因为它假定了数据类型的指针与函数指针的存储宽度是相等的,虽然尽管目前大部分 PC 的 CPU 支持这一假设,但是在 C99 标准里,使用无类型的数据指针指向函数,这属于未定义的行为

GClosure 结构体主要是面向各种语言对闭包的公有功能进行了基本抽象,这样所有要与 GObject 闭包机制打交道的语言,可以先继承 GClosure 结构体,然后再根据自身需要再添加一些特定的数据成员。例如,GCClosure 添加了 callback 无类型数据指针,用于指向闭包所对应的回调函数。

GClosure 结构体中包含许多成员,其中对于使用者较为重要的三个成员是 marshal、data 与 marshal_data,其中 marshal 是一个函数指针,指向一个回调函数的调用者;data 与 marshal_data 皆为 gpointer 指针类型。对于 C 语言闭包,data 指向用户向回调函数传递的数据地址,而 marshal_data 则指向回调函数。我知道,这句话也许会引起下面的问答。

Q:那个 GCClosure 结构体中不是已经有了一个指向回调函数的 callback 指针吗?

A:这是因为callback 所指向的函数是闭包默认的回调函数,而 GObject 库允许你自由切换回调函数,可将 marshal_data 看作是一个开关,它可以暂时屏蔽 GCClosure 中的 callback 所指向的回调函数,而启用 marshal_data 所指向的回调函数。事后,可以调用 g_closure_set_meta_marshal 函数将 marshal_data 所指向的回调函数屏蔽,这样闭包的回调函数又被切换为 callback 所指向的回调函数了。下文会给出一个具体的示例来验证这一说法。

从 GObject 库使用者的角度,我们对 GClosure 结构体作用的理解在此止步即可。如果像文档 [1] 那样的深入挖掘,那么就好比是一个普通人在不具备医生心理素质的情况下解剖任何一种动物,恶心是难免的。

我们将话题转回刚才所讨论的 main 函数中的代码片段。看下面的代码:

GClosure *closure = g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

它们一起实现了下图所示的数据结构:

紧接着,将所建立的闭包 closure 作为第一个参数,浮点数变量 b作为第 2 个参数,代入函数 compare,即:

compare (closure, &b);

因为 closure 中即包含了浮点数变量 a,也包含了浮点数比较函数 float_compare,因此在 compare 函数中,使用 float_compare 函数可进行浮点数 a 与 b 大小比较,是显而易见的。但事实上,在 compare 函数中,我们并没有直接调用 float_compare 函数,而是将这个任务交给了 g_closure_invoke 函数。

来看 compare 函数的主干部分:

static void compare(GClosure *a, void *b)
{
        ... ...
 
        g_closure_invoke (a, &return_value, 1, &param_value, NULL);
        gint r = g_value_get_int (&return_value);
 
        ... ...
}

由于 compare 函数的第一个参数是闭包 closure,它已经包含了浮点数变量 a 和回调函数 float_compare。当我们将 closure 作为 g_closure_invoke 函数的参数时,后者会基于 closure 中的 marshal 指针调用 g_closure_user_marshal_INT__VOID_VOID 函数,而 g_closure_user_marshal_INT__VOID_VOID 函数是我们自己定义的,对于本文示例而言其主干部分如下:

void g_cclosure_user_marshal_INT__VOID_VOID( ... )
{
        ... ... ...
 
        if (G_CCLOSURE_SWAP_DATA (closure))
        {
                data1 = closure->data;
                data2 = g_value_peek_pointer (param_values + 0);
        }
 
        ... ... ...
 
        callback = (GMarshalFunc_INT__VOID_VOID) (
                marshal_data ? marshal_data : cc->callback);
 
        v_return = callback (data1, data2);
 
        ... ... ...
}

上述代码所做的工作无非是从 closure 闭包中先获取浮点数变量 a,即 data1,然后从 g_cclosure_user_marshal_INT__VOID_VOID 函数的参数中获取浮点数变量 b,即 data2,然后再从 closure 闭包中获得 callback 指针所指向的回调函数,即 float_compare,最终完成浮点数 a 与 b 的大小比较。

闭包调用的整个过程大致如下图所示:

GClosure 闭包的工作过程非常繁琐,它所做的主要工作就是向回调函数的调用过程中插入两个环节,即 g_closure_invoke 函数与 g_closure_user_marshal_*__** 函数。这样做的主要目的是提高闭包的灵活性。就像是链条,如果只有 2 个节点,那么它只能像一根直线段那样生存,如果将其改造成 4 个节点,它就变成了一条 3 次曲线了!

更换 marshal
前文说过,GClosure 结构体中有一个 marshal_data 指针,它也可以指向一个回调函数,并且会取代 GCClosure 结构体中 callback 指针所指向的回调函数。为了充分说明这一点,我们对前文的 GClosure 示例进行一些修改,如下:

#include <math.h>
#include <glib-object.h>
 
void g_cclosure_user_marshal_INT__VOID_VOID(GClosure     *closure,
                                        GValue       *return_value G_GNUC_UNUSED,
                                        guint         n_param_values,
                                        const GValue *param_values,
                                        gpointer      invocation_hint G_GNUC_UNUSED,
                                        gpointer      marshal_data)
{
        typedef gint (*GMarshalFunc_INT__VOID_VOID) (gpointer     data1,
                                                     gpointer     data2);
        register GMarshalFunc_INT__VOID_VOID callback;
        register GCClosure *cc = (GCClosure*) closure;
        register gpointer data1, data2;
        gint v_return;
 
        g_return_if_fail (return_value != NULL);
        g_return_if_fail (n_param_values == 1);
 
        if (G_CCLOSURE_SWAP_DATA (closure))
        {
                data1 = closure->data;
                data2 = g_value_peek_pointer (param_values + 0);
        }
        else
        {
                data1 = g_value_peek_pointer (param_values + 0);
                data2 = closure->data;
        }
        callback = (GMarshalFunc_INT__VOID_VOID) (
                marshal_data ? marshal_data : cc->callback);
 
        v_return = callback (data1, data2);
 
        g_value_set_int (return_value, v_return);
}
 
static void compare(GClosure *closure, void *b)
{
        GValue return_value = {0};
        GValue param_value  = {0};
        g_value_init (&return_value, G_TYPE_INT);
        g_value_init (&param_value, G_TYPE_POINTER);
 
        g_value_set_pointer (&param_value, b);
 
        g_closure_invoke (closure, &return_value, 1, &param_value, NULL);
        gint r = g_value_get_int (&return_value);
 
        if (r == -1)
                g_print ("a < b\n");
        else if (r == 0)
                g_print ("a = b\n");
        else
                g_print ("a > b\n");
 
        g_value_unset (&return_value);
        g_value_unset (&param_value);
}
 
 
static gint str_compare(void *a, void *b)
{
        size_t len1 = g_utf8_strlen ((gchar *)a, -1);
        size_t len2 = g_utf8_strlen ((gchar *)b, -1);
 
        if (len1 > len2)
                return 1;
        else if (len1 == len2)
                return 0;
        else
                return -1;
}
 
static gint str_compare_new(void *a, void *b)
{
        g_print ("\nI'm a new marshaller\n");
 
        return (str_compare (a, b));
}
 
int main(void)
{
        g_type_init ();
 
        gchar *s1 = "Hello World!\n";
        gchar *s2 = "Hello!\n";
 
        GClosure *closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);
        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, s2);
 
        g_closure_set_meta_marshal (closure, str_compare_new,
                                    g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, s2); 
 
        g_closure_unref (closure);
 
        return 0;
}

上述代码所做的修改,主要是删除了有关浮点数比较的示例部分,然后添加了一个新函数 str_compare_new,并对 main 函数中字符串比较部分的代码做了变动,重点观察以下代码:

/* main 函数代码片段 */
        GClosure *closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);
        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, s2);
 
        g_closure_set_meta_marshal (closure, str_compare_new,
                                    g_cclosure_user_marshal_INT__VOID_VOID);
        compare (closure, s2);

第一次调用 compare 函数,闭包函数调用链的终端是 str_compare 函数,而第二次调用 compare 函数时,闭包函数调用链的终端是 str_compare_new 函数,它再去调用 str_compare 实现字串比较。这是因为,在第二次调用 compare 函数之前,我们通过 g_closure_set_meta_marshal 函数设置了 closure 闭包的 marshal_data 指针,使其指向了 str_compare_new 函数。这样,在 g_cclosure_user_marshal_INT__VOID_VOID 函数中,代码:

callback = (GMarshalFunc_INT__VOID_VOID) (marshal_data ? marshal_data : cc->callback);

callback 的值会是 marshal_data,而非 cc->callback。

如何又快又好的产生 g_cclosure_user_marshal_* 函数?
GLib 库提供了一个名为 glib-genmarshal 的工具,它可以根据我们给出的函数描述信息产生有效的 marshal 代码。上文中的 g_cclosure_user_marshal_INT__VOID_VOID 函数,我便是使用这个工具产生的。

首先,准备好一份文本文档,例如 in__void_void.txt:

INT:VOID,VOID

然后,执行命令:

glib-genmarshal --body int__void_void.txt > int__void_void.c

即可产生 g_cclosure_user_marshal_INT__VOID_VOID 函数。

另外,GObject 也预定义了一组 marshal 函数,可参考文档 [2] 中所有 g_cclosure_marshal_ 为前缀的函数。

参考文档
[1] 恶心的GObject[Part I][v0.1]
[2] GObject 手册的 GClosure 部分

Over!

From: http://garfileo.is-programmer.com/2011/3/20/function-pointer-and-callback-function-and-closure.25453.html

Linux 应用程序开发之设置用户和组

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
 
...
 
gchar *user = NULL;
gchar *group = NULL;
 
...
 
if(group)
{
    struct group *grp = NULL;
 
    if(NULL == (grp = getgrnam(group)))
      g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                  __FUNCTION__, "Get group failed!");
 
    if(-1 == setgid(grp->gr_gid))
      g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                  __FUNCTION__, "Set gid failed!");
    if(-1 == setgroups(0, NULL))
      g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                  __FUNCTION__, "Set groups failed!");
    if(user)
    {
        if(-1 == initgroups(user, grp->gr_gid))
          g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                      __FUNCTION__, "Init groups failed!");
    }
}
 
if(user)
{
    struct passwd *pwd = NULL;
 
    if(NULL == (pwd = getpwnam(user)))
      g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                  __FUNCTION__, "Get user failed!");
 
    if(-1 == setuid(pwd->pw_uid))
      g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__,
                  __FUNCTION__, "Set uid failed!");
}
 
...

Over!

Linux 内核启动 Logo 居中显示

Linux 内核启动 Logo 居中显示需要修改下列两个源文件:
1. drivers/video/console/fbcon.c

diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 10c0b74..cdb3b6c 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -589,6 +589,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
        if (fb_get_color_depth(&info->var, &info->fix) == 1)
                erase &= ~0x400;
        logo_height = fb_prepare_logo(info, ops->rotate);
+       logo_height += (info->var.yres - 110) / 2;
        logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
        q = (unsigned short *) (vc->vc_origin +
                                vc->vc_size_row * rows);

2. drivers/video/fbmem.c

diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index b066475..c8e7fc0 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -468,8 +468,8 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
                fb_set_logo(info, logo, logo_new, fb_logo.depth);
        }
 
-       image.dx = 0;
-       image.dy = y;
+       image.dx = (info->var.xres - 400) / 2;
+       image.dy = (info->var.yres - 110) / 2 - 70;
        image.width = logo->width;
        image.height = logo->height;

Over!

基于 GObject 的 UDisks 库 [开发中]

The udisks project provides:

  • a daemon, udisksd, that implements well-defined D-Bus interfaces that can be used to query and manipulate storage devices.
  • a command-line tool, udisksctl, that can be used to query and use the daemon

The actions that a user can perform using udisks are restricted using polkit.

Git repository: http://gitcafe.com/heiher/hev-udisks-library

Over!

Create your own boot logo

Preparations

  • A complete system using buildroot
  • You need a board with the U-boot bootloader downloaded in NAND Flash.

Developping the boot logo
Linux users can use the Gimp for creating a logo. Gimp is an open source alternative for expensive Programs like Photoshop,… etc. On the Ftp website you can find a simple image I created in Gimp:

ftp://wikiusers@ftp.arm9board.net/Kernel_logo (password: Arm9board)

You find an .xcf and a .ppm file. Best is to download the .xcf file and open it using Gimp image editor.

As you can see it’s a pretty simple image, but it can be used for tutorial purposes. As you can see I’m using the 4.3″ LCD which has a 480*272 resolution. For other resolutions, you’ll have to adapt the image’s size of course.

Converting the image
When you’ve adapted the image as you please, you can save it using another extension .ppm and give the file the name:

logo_linux_custom.ppm

Going to a 224 color image
Now to fulfill the Linux kernels requirements, we need to adapt the ppm file a little bit more. The kernel boot screens only use 224 color images. So to convert our image, let’s open a new terminal and type:

sudo apt-get install netpbm

Reduce the colors to 224 with the following command:

ppmquant 224 logo_linux_custom.ppm > logo_linux_custom_224.ppm

Put the last file into ASCII mode:

pnmnoraw logo_linux_custom_224.ppm > logo_linux_custom_ascii_224.ppm

Put it inside the Kernel image
Move inside the buildroot directory:

cd ~/buildroot-2011.05
make clean

This will remove any build data, so we can restart with a fresh unpacked directory of the kernel. Edit the menuconfig of the kernel to unzip the archive:

make linux26-menuconfig

and exit the menuconfig. Now move into the unpacked directory of the kernel:

cd output/build/linux-custom/drivers/video/

And copy the logo_linux_custom_ascii_224.ppm to the logo directory:

cp logo_linux_custom_ascii_224.ppm logo/logo_custom_clut224.ppm

IMPORTANT: the _clut224 ending is very important for the kernel!

Editing the necessary files
Move into the logo directory:

cd logo

Edit the Makefile

gedit Makefile

And around line 19 add the followin line:

obj-$(CONFIG_LOGO_ARMADEUS_CLUT224)     += logo_custom_clut224.o

Edit the logo.c file

gedit logo.c

And add the following line somewhere between all the _CLUT224 ifdef routines:

#ifdef CONFIG_LOGO_ARMADEUS_CLUT224
    /* Armadeus Linux logo */
    logo = &logo_custom_clut224;
#endif

Edit the Kconfig file

gedit Kconfig

And add the following lines to add your own boot logo to the menuconfig, of course you can change the text to whatever you want:

config LOGO_CUSTOM_CLUT224
 	bool "Your own boot logo here"
	default y

Editing the header file
This depends on which kernel version you are using:
For the 2.6.28 kernel version
You need to reopen the logo.c file again:

gedit logo.c

And add the following header around line 39:

extern const struct linux_logo logo_custom_clut224;

For the 2.6.36 kernel version
Move into the include directory of the linux kernel:

cd ~/buildroot-2011.05/output/build/linux-custom/include/linux
gedit linux_logo.h

And around line 50, add the following line:

extern const struct linux_logo logo_custom_clut224;

Select your boot logo and compile
Inside the main directory of buildroot, type:

make linux26-menuconfig

and select:

Device Drivers  --->
    Graphics support  --->
       [*] Bootup logo  --->
           [*] Your own boot logo here

Exit the menuconfig and build the new system

make

Download the zImage to the board an you’ll see the new boot logo appearing!

Removing the blinking cursor
You’ll see that in the top left corner, the cursor keeps blinking, we can remove it by makeing the following change to the kernel source code:
For the 2.6.36 kernel version

gedit ~/buildroot-2011.05/output/build/linux-custom/drivers/char/vt.c

Look for line 1640 and change:

vc->vc_deccm		= global_cursor_default;
to
vc->vc_deccm            = 0;

For the 2.6.28 kernel version

gedit ~/buildroot-2011.05/output/build/linux-custom/drivers/char/vt.c

Look for line 1623 and change:

vc->vc_deccm		= 1;
to
vc->vc_deccm		= 0;

Getting the cursor to blink again from a terminal
To activate the cursor again, you can boot your system and in a minicom terminal, type:

echo -e '\033[?25h' > /dev/tty1
echo 1 > /sys/class/graphics/fbcon/cursor_blink

Important notice
Remember that for now we have change the output build directory from buildroot, which means that when you use

make clean

All the changes you made will be lost, because buildroot starts again from the archive inside the dl folder
Best is to create a new archive from the output/build/linux-custom directory and put it inside the dl folder or to make all the changes we’ve just made directly inside the kernel .tgz archive inside the dl folder.

Have fun!

From: http://www.arm9board.net/wiki/index.php?title=Create_your_own_boot_logo

Over!

TSLib 增加校准后无需重启图形系统自动应用功能

直接上补丁喽

diff --git a/plugins/linear.c b/plugins/linear.c
index a24916a..90fc985 100644
--- a/plugins/linear.c
+++ b/plugins/linear.c
@@ -16,12 +16,15 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <time.h>
 
 #include <stdio.h>
 
 #include "tslib.h"
 #include "tslib-filter.h"
 
+#define TSLIB_LINEAR_TIMEOUT	5
+
 struct tslib_linear {
 	struct tslib_module_info module;
 	int	swap_xy;
@@ -33,14 +36,74 @@ struct tslib_linear {
 
 // Linear scaling and offset parameters for x,y (can include rotation)
 	int	a[7];
+
+// Timespec
+	struct timespec last;
 };
 
+static void
+linear_read_pointercal(struct tslib_linear *lin)
+{
+	struct stat sbuf;
+	int pcal_fd;
+	char pcalbuf[200];
+	int index;
+	char *tokptr;
+	char *calfile=NULL;
+	char *defaultcalfile = "/etc/pointercal";
+
+// Use default values that leave ts numbers unchanged after transform
+	lin->a[0] = 1;
+	lin->a[1] = 0;
+	lin->a[2] = 0;
+	lin->a[3] = 0;
+	lin->a[4] = 1;
+	lin->a[5] = 0;
+	lin->a[6] = 1;
+	lin->p_offset = 0;
+	lin->p_mult   = 1;
+	lin->p_div    = 1;
+	lin->swap_xy  = 0;
+
+	/*
+	 * Check calibration file
+	 */
+	if( (calfile = getenv("TSLIB_CALIBFILE")) == NULL) calfile = defaultcalfile;
+	if(stat(calfile,&sbuf)==0) {
+		pcal_fd = open(calfile,O_RDONLY);
+		read(pcal_fd,pcalbuf,200);
+		lin->a[0] = atoi(strtok(pcalbuf," "));
+		index=1;
+		while(index<7) {
+			tokptr = strtok(NULL," ");
+			if(*tokptr!='\0') {
+				lin->a[index] = atoi(tokptr);
+				index++;
+			}
+		}
+#ifdef DEBUG
+		printf("Linear calibration constants: ");
+		for(index=0;index<7;index++) printf("%d ",lin->a[index]);
+		printf("\n");
+#endif /*DEBUG*/
+		close(pcal_fd);
+	}
+}
+
 static int
 linear_read(struct tslib_module_info *info, struct ts_sample *samp, int nr)
 {
 	struct tslib_linear *lin = (struct tslib_linear *)info;
 	int ret;
 	int xtemp,ytemp;
+	struct timespec now;
+
+	clock_gettime(CLOCK_MONOTONIC, &now);
+	if(TSLIB_LINEAR_TIMEOUT < (now.tv_sec-lin->last.tv_sec))
+	{
+		linear_read_pointercal(lin);
+		memmove(&(lin->last), &now, sizeof(struct timespec));
+	}
 
 	ret = info->next->ops->read(info->next, samp, nr);
 	if (ret >= 0) {
@@ -102,13 +165,6 @@ TSAPI struct tslib_module_info *mod_init(struct tsdev *dev, const char *params)
 {
 
 	struct tslib_linear *lin;
-	struct stat sbuf;
-	int pcal_fd;
-	char pcalbuf[200];
-	int index;
-	char *tokptr;
-	char *calfile=NULL;
-	char *defaultcalfile = "/etc/pointercal";
 
 	lin = malloc(sizeof(struct tslib_linear));
 	if (lin == NULL)
@@ -116,45 +172,10 @@ TSAPI struct tslib_module_info *mod_init(struct tsdev *dev, const char *params)
 
 	lin->module.ops = &linear_ops;
 
-// Use default values that leave ts numbers unchanged after transform
-	lin->a[0] = 1;
-	lin->a[1] = 0;
-	lin->a[2] = 0;
-	lin->a[3] = 0;
-	lin->a[4] = 1;
-	lin->a[5] = 0;
-	lin->a[6] = 1;
-	lin->p_offset = 0;
-	lin->p_mult   = 1;
-	lin->p_div    = 1;
-	lin->swap_xy  = 0;
+	clock_gettime(CLOCK_MONOTONIC, &(lin->last));
+	linear_read_pointercal(lin);
 
 	/*
-	 * Check calibration file
-	 */
-	if( (calfile = getenv("TSLIB_CALIBFILE")) == NULL) calfile = defaultcalfile;
-	if(stat(calfile,&sbuf)==0) {
-		pcal_fd = open(calfile,O_RDONLY);
-		read(pcal_fd,pcalbuf,200);
-		lin->a[0] = atoi(strtok(pcalbuf," "));
-		index=1;
-		while(index<7) {
-			tokptr = strtok(NULL," ");
-			if(*tokptr!='\0') {
-				lin->a[index] = atoi(tokptr);
-				index++;
-			}
-		}
-#ifdef DEBUG
-		printf("Linear calibration constants: ");
-		for(index=0;index<7;index++) printf("%d ",lin->a[index]);
-		printf("\n");
-#endif /*DEBUG*/
-		close(pcal_fd);
-	}
-		
-		
-	/*
 	 * Parse the parameters.
 	 */
 	if (tslib_parse_vars(&lin->module, linear_vars, NR_VARS, params)) {

Over!

使用 GFile 异步复制文件并显示进度

源代码

/* gfile-copy.c
 * Heiher <admin@heiher.info>
 */
 
#include <gtk/gtk.h>
#include <gio/gio.h>
 
static gboolean timeout_handler(gpointer user_data)
{
	gtk_main_quit();
 
	return FALSE;
}
 
static void g_file_copy_progress_handler(goffset current_num_bytes,
			goffset total_num_bytes, gpointer user_data)
{
	GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(user_data);
	gchar buf[1024] = { 0 };
 
	g_sprintf(buf, "%ld KB / %ld KB", current_num_bytes / 1024,
				total_num_bytes / 1024);
	gtk_progress_bar_set_text(progress_bar, buf);
	gtk_progress_bar_set_fraction(progress_bar, (gdouble)current_num_bytes / (gdouble)total_num_bytes);
}
 
static void g_file_copy_async_ready_handler(GObject *source_object,
			GAsyncResult *res, gpointer user_data)
{
	GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(user_data);
 
	g_file_copy_finish(G_FILE(source_object), res, NULL);
	gtk_progress_bar_set_text(progress_bar, "Finished");
	gtk_progress_bar_set_fraction(progress_bar, 1.0);
 
	g_timeout_add_seconds(2, timeout_handler, NULL);
}
 
int main(int argc, char *argv[])
{
	GtkWidget *window = NULL;
	GtkWidget *progress_bar = NULL;
	GFile *src = NULL, *dst = NULL;
 
	gtk_init(&argc, &argv);
 
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request(window, 400, 30);
	gtk_widget_show(window);
 
	progress_bar = gtk_progress_bar_new();
	gtk_container_add(GTK_CONTAINER(window),
				progress_bar);
	gtk_widget_show(progress_bar);
 
	src = g_file_new_for_path("/tmp/src.img");
	dst = g_file_new_for_path("/tmp/dst.img");
 
	g_file_copy_async(src, dst, G_FILE_COPY_OVERWRITE,
				G_PRIORITY_DEFAULT, NULL, g_file_copy_progress_handler,
				progress_bar, g_file_copy_async_ready_handler, progress_bar);
 
	gtk_main();
 
	return 0;
}

编译、执行

gcc -o gfile-copy gfile-copy.c `pkg-config --cflags --libs gtk+-2.0 gio-2.0`
./gfile-copy

Over!

X Window 禁用显示器的节能模式

在没有安装桌面环境的电源管理程序(如 gnome-power-manager)的情况下,可以通过 X Window 的 DPMS 控制选项来开启、关闭显示器的节能模式。禁用 DPMS 功能是无法禁用节能模式的。

查看当前 DPMS 状态

...
 
DPMS (Energy Star):
  Standby: 600    Suspend: 600    Off: 600
  DPMS is Enabled
  Monitor is On

禁用节能模式
编辑文件 /etx/X11/xorg.conf

...
 
Section "ServerFlags"
    Option "BlankTime" "0"
    Option "StandbyTime" "0"
    Option "SuspendTime" "0"
    Option "OffTime" "0"
EndSection
 
...

Over!