HevSCGIServer 混合语言开发

HevSCGIServerLibrary 实现了 GObject Introspection 支持且生成了 Vala API。从此,HevSCGIServer 框架支持混合语言编程了,理论上只要支持 GObject Introspection 的语言都可以用来开发。

目前,存在一个 Vala 语言编写的实例程序 HevSCGIServerVala,功能与 C 语言实现的 HevSCGIServerHello 一样。

下一步将通过模块的形式实现一个 JavaScript 语言的解释器,将意味着 Web Application 可以使用 JavaScript 语言编写,要注意的是它可以使用几乎所有的系统库哦。(Gjs 通过 GObject Introspection 可调用几乎所有的库)

Over!

GLib 创建自定义事件源

GLib 实现了一个功能强大的事件循环分发处理机制,被抽象成为 GMainLoop,用于循环处理事件源上的事件。每个 GMainLoop 都工作在指定的 GMainContext 上。事件源在 GLib 中则被抽象成了 GSource。在 GMainContext 中有一个 GSource 列表。GLib 内部定义实现了三种类型的事件源,分别是 Idle, Timeout 和 I/O。同时也支持创建自定义的事件源。

自定义事件源的基本作用
自定义的事件源可以用来将外部信号(事件)挂到程序中的指定主循环上,从而在 g_main_loop_run 中可以响应这些事件。

如何创建自定义事件源
GLib 提供了一系列的接口用于创建自定义的事件源,下面我们先讲解一下创建事件源的基本函数和数据结构,最后给出一些实例。

自定义的事件源是一个继承 GSource 的结构体,即自定义事件源的结构体 的第一个成员是 GSource 结构体, 其后便可放置程序所需数据, 例如:

typedef struct _MySource MySource;
 
struct _MySource
{
    GSource _source;
    gchar text[256];
}

实现了事件源数据结构的定义之后,还需要实现事件源所规定的接口,主要为 prepare, check, dispatch, finalize 等事件处理函数(回调函数),它们包含于 GSourceFuncs 结构体中。将 GSourceFuncs 结构以及事件源结构的存储空间宽度作为参数传给 g_source_new 便可构造一个新的事件源,继而可使用 g_source_attach 函数将新的事件源添加到主循环上下文中。下面这个示例可创建一个只会讲“Hello world!”的事件源,并将其添加到主事件循环默认的 GMainContext 中。

#include <glib.h>
#include <glib/gprintf.h>
 
typedef struct _MySource MySource;
struct _MySource
{
    GSource source;
    gchar text[256];
};
 
static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = 0;
 
    return TRUE;
}
 
static gboolean check(GSource *source)
{
    return TRUE;
}
 
static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    MySource *mysource = (MySource *)source;
 
    g_print("%s\n", mysource->text);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
    GSourceFuncs source_funcs = {prepare, check, dispatch, NULL};
    GSource *source = g_source_new(&source_funcs, sizeof(MySource));
 
    g_sprintf(((MySource *)source)->text, "Hello world!");
    g_source_attach(source, context);
    g_source_unref(source);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

上述程序的 g_main_loop_run 函数运行时,会迭代访问 GMainContext 的事件源列表,步骤大致如下:
a. g_main_loop_run 通过调用事件源的 prepare 接口并判断其返回值以确定各事件源是否作好准备。如果各事件源的 prepare 接口的返回值为 TRUE,即表示该事件源已经作好准备,否则表示尚未做好准备。显然,上述程序所定义的事件源是已经作好了准备。
b. 若某事件源尚未作好准备 ,那么 g_main_loop 会在处理完那些已经准备好的事件后再次询问该事件源是否作好准备 ,这一过程是通过调用事件源的 check 接口而实现的,如果事件源依然未作好准备,即 check 接口的返回 FALSE,那么 g_main_loop_run 会让主事件循环进入睡眠状态。主事件循环的睡眠时间是步骤 a 中遍历时间源时所统计的最小时间间隔 ,例如在 prepare 接口中可以像下面这样设置时间间隔。到达一定时间后, g_main_loop_run 会唤醒主事件循环,再次询问。如此周而复始,直至事件源的 prepare 接口返回值为 TRUE。

static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = 1000; /* set time interval one second */
 
    return TRUE;
}

c. 若事件源 prepare 与 check 函数返回值均为 TRUE,则 g_main_loop_run 会调用事件源的 dispatch 接口,由该接口调用事件源的响应函数。事件源的响应函数是回调函数,可使用 g_source_set_callback 函数进行设定。在上例中, 我们没有为自定义的事件源提供响应函数。

上文自定义的事件源实际是 Idle 类型的,此类事件源,是指那些只有在主事件循环无其他事件源处理时才会被处理的事件源。GLib 提供了预定义的空闲事件源类型,其用法见下面的示例。

#include <glib.h>
 
static gboolean idle_func(gpointer data)
{
    g_print("%s\n", (gchar *)data);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
 
    g_idle_add(idle_func, "Hello world!");
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

上述示例中,idle_func 是 idle 事件源的响应函数,如果该函数返回值为 TRUE,那么它会在主事件循环空闲时重复被执行;如果 idle_func 的返回值为 FALSE,那么该函数在执行一次后,便被主事件循环从事件源中移除。g_idle_add 函数内部定义了一个空闲事件源,并将用户定义的回调函数设为空闲事件源的响应函数, 然后将该事件源挂到主循环上下文。

Timeout 类事件源,GLib 也提供了预定义的定时器事件源,其用法与 GLib 预定义的空闲事件源类似。例如:

#include <glib.h>
 
static gboolean timeout_func(gpointer data)
{
    static guint i = 0;
 
    i += 2;
    g_print ("%d\n", i);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
 
    g_timeout_add(2000, timeout_func, loop);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

如果要自定义定时器类型的事件源,只需让事件源的 prepare 与 check 接口在时间超过所设定的时间间隔时返回 TRUE, 否则返回 FALSE。

I/O 类型的事件源要稍微难理解一些,因为涉及到了操作系统层面的 poll 机制。所谓 poll 机制,就是操作系统提供的对文件描述符所关联的 I/O 的状态监视功能 ,例如向文件中写入数据 ,那么 I/O 的状态可以表示为 POLLOUT, 而从文件中读取数据,那么 I/O 的状态就变为 POLLIN。GLib 为 Unix 系统与Windows 系统的 poll 机制进行了封装,并且可以将文件与主事件循环的事件源建立关联,在主循环的过程中, g_main_loop_run 会轮询各个关联到文件的事件源,并处理相应的事件响应。I/O 类型的事件源, prepare,其 check, dispatch 等接口的执行次序如下:

a. 主事件循环会首先调用 check 接口, 询问事件源是否准备好。因为此时, g_main_loop_run 尚未轮询那些与 I/O 相关联的事件源, 所以 I/O 类型的事件源, check 接口的返回值应该是 FALSE。其主事件循环调用 g_main_context_iteration 轮询各事件源,探寻是否有 I/O 类型事件源的状态发生变化,并记录变化结果。
b. 主循环调用 check 接口, 询问事件是否准备好。此时, 如果 I/O 类型事件源的状态变化符合要求,那么就返回 TRUEE,否则返回 FALSE。
c. 如果 prepare 与 check接口的返回值均为 TRUE, 那么此时主事件循环会调用 dispatch 接口分发消息。

下面的示例展示了一个自定义的 I/O 类型事件源的基本用法。该示例所产生的程序接受用户在终端中输入的字符串,并统计输入的字符数量。

#include <glib.h>
 
typedef struct _MySource MySource;
 
struct _MySource
{
    GSource _source;
    GIOChannel *channel;
    GPollFD fd;
};
 
static gboolean watch(GIOChannel *channel)
{
    gsize len = 0;
    gchar *buffer = NULL;
 
    g_io_channel_read_line(channel, &buffer, &len, NULL, NULL);
    if(len > 0)
      g_print("%d\n", len);
    g_free(buffer);
 
    return TRUE;
}
 
static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = -1;
    return FALSE;
}
 
static gboolean check(GSource *source)
{
    MySource *mysource = (MySource *)source;
 
    if(mysource->fd.revents != mysource->fd.events)
      return FALSE;
 
    return TRUE;
}
 
static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    MySource *mysource = (MySource *)source;
 
    if(callback)
      callback(mysource->channel);
 
    return TRUE;
}
 
static void finalize(GSource *source)
{
    MySource *mysource = (MySource *)source;
 
    if(mysource->channel)
      g_io_channel_unref(mysource->channel);
}
 
int main(int argc, char* argv[])
{
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    GSourceFuncs funcs = {prepare, check, dispatch, finalize};
    GSource *source = g_source_new(&funcs, sizeof(MySource));
    MySource *mysource = (MySource *)source;
 
    mysource->channel = g_io_channel_new_file("test", "r", NULL);
    mysource->fd.fd = g_io_channel_unix_get_fd(mysource->channel);
    mysource->fd.events = G_IO_IN;
    g_source_add_poll(source, &mysource->fd);
    g_source_set_callback(source, (GSourceFunc)watch, NULL, NULL);
    g_source_set_priority(source, G_PRIORITY_DEFAULT_IDLE);
    g_source_attach(source, NULL);
    g_source_unref(source);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(g_main_loop_get_context(loop));
    g_main_loop_unref(loop);
 
    return 0;
}

像 Idle 类型与 Timeout 类型事件源那样,GLib 也提供了预定义的 I/O 类型事件源,使用它可以将上例简化为:

#include <glib.h>
 
gboolean io_watch(GIOChannel *channel, GIOCondition condition, gpointer data)
{
    gsize len = 0;
    gchar *buffer = NULL;
 
    g_io_channel_read_line(channel, &buffer, &len, NULL, NULL);
    if(len > 0)
      g_print("%d\n", len);
    g_free(buffer);
 
    return TRUE;
}
 
int main(int argc, char* argv[])
{
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    GIOChannel* channel = g_io_channel_unix_new(1);
 
    if(channel)
    {
        g_io_add_watch(channel, G_IO_IN, io_watch, NULL);
        g_io_channel_unref(channel);
    }
 
    g_main_loop_run(loop);
 
    g_main_context_unref(g_main_loop_get_context(loop));
    g_main_loop_unref(loop);
 
    return 0;
}

Over!

一个实现 GAsyncInitable 接口类的例子

GAsyncInitable 是一个支持异步的、可失败的对象初始化接口类,开发时需要支持异步创建对象并存在失败的可能(多数用于包含 I/O 操作的对象)即可实现此接口类。

完整的示例代码

HevIObj 类是基于 GObject 类的,类中对象实例的创建有两个方法:

void hev_iobj_new_async(GCancellable *cancellable,
                        GAsyncReadyCallback callback, gpointer user_data);
GObject * hev_iobj_new_finish(GAsyncInitable *initable,
                        GAsyncResult *res, GError **error);

对象实例化时先通过调用 hev_iobj_new_async 方法并传入回调函数,当对象初始化好后传入的回调函数将被调用,在回调函数中再调用对象的 hev_iobj_new_finish 方法即可得到类的新对象实例。

接口类的实现

static void hev_iobj_async_initable_iface_init(GAsyncInitableIface *iface);
static void hev_iobj_async_initable_init_async(GAsyncInitable *initable,
                        gint io_priority, GCancellable *cancellable,
                        GAsyncReadyCallback callback, gpointer user_data);
static gboolean hev_iobj_async_initable_init_finish(GAsyncInitable *initable,
                        GAsyncResult *result, GError **error);
 
G_DEFINE_TYPE_WITH_CODE(HevIObj, hev_iobj, G_TYPE_OBJECT,
                G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, hev_iobj_async_initable_iface_init));

与实现其它接口类没有什么区别,通过宏 G_IMPLEMENT_INTERFACE 格式的写入接口类型和接口类初始化函数,hev_iobj_async_initable_iface_init 接口类初始化函数中进行接口方法的重载:

static void hev_iobj_async_initable_iface_init(GAsyncInitableIface *iface)
{
        g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__);
 
        iface->init_async = hev_iobj_async_initable_init_async;
        iface->init_finish = hev_iobj_async_initable_init_finish;
}

重载的异步实例初始化函数 hev_iobj_async_initable_init_async 将在 hev_iobj_new_async 方法后被调用,它先创建一个 GSimpleAsyncResult 实例,用于返回异步操作的结果。如果异步操作支持取消,g_simple_async_result_set_check_cancellable 应该在 GSimpleAsyncResult 实例创建后立即调用,GIO 的异步操作实例是通过线程的方式实现的,g_simple_async_result_run_in_thread 方法则是创建一个新的线程并执行异步操作,传入的线程的处理函数完成后 hev_iobj_async_initable_init_finish 即被调用。详见示例代码:

static void hev_iobj_async_initable_init_async(GAsyncInitable *initable,
                        gint io_priority, GCancellable *cancellable,
                        GAsyncReadyCallback callback, gpointer user_data)
{
        HevIObj *self = HEV_IOBJ(initable);
        HevIObjPrivate *priv = HEV_IOBJ_GET_PRIVATE(self);
        GSimpleAsyncResult *simple = NULL;
 
        g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__);
 
        simple = g_simple_async_result_new(G_OBJECT(initable),
                                callback, user_data, hev_iobj_async_initable_init_async);
        g_simple_async_result_set_check_cancellable(simple, cancellable);
        g_simple_async_result_run_in_thread(simple, async_result_run_in_thread_handler,
                                io_priority, cancellable);
        g_object_unref(simple);
}

当异步操作完成后, hev_iobj_async_initable_init_finish 将被调用,在里面可通过 GError 向外抛出错误(如果出错 hev_iobj_new_finish 方法传入的 error 将被填充),并通过此函数的返回值控制对象的初始化是否完成。

static gboolean hev_iobj_async_initable_init_finish(GAsyncInitable *initable,
                        GAsyncResult *result, GError **error)
{
        g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__);
 
        g_return_val_if_fail(g_simple_async_result_is_valid(result,
                                        G_OBJECT(initable), hev_iobj_async_initable_init_async),
                                FALSE);
        if(g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
                                        error))
          return FALSE;
 
        return TRUE;
}

Debug 信息显示出的对象创建和销毁的整个流程

** (process:21875): DEBUG: hev-iobj.c:137[hev_iobj_new_async]
** (process:21875): DEBUG: hev-iobj.c:70[hev_iobj_class_init]
** (process:21875): DEBUG: hev-iobj.c:82[hev_iobj_async_initable_iface_init]
** (process:21875): DEBUG: hev-iobj.c:54[hev_iobj_constructor]
** (process:21875): DEBUG: hev-iobj.c:92[hev_iobj_init]
** (process:21875): DEBUG: hev-iobj.c:61[hev_iobj_constructed]
** (process:21875): DEBUG: hev-iobj.c:109[hev_iobj_async_initable_init_async]
** (process:21875): DEBUG: hev-iobj.c:98[async_result_run_in_thread_handler]
** (process:21875): DEBUG: hev-iobj.c:146[hev_iobj_new_finish]
** (process:21875): DEBUG: hev-iobj.c:122[hev_iobj_async_initable_init_finish]
** (process:21875): DEBUG: hev-iobj.c:37[hev_iobj_dispose]
** (process:21875): DEBUG: hev-iobj.c:47[hev_iobj_finalize]

Over!

使用 GtkBuilder 构造自定义构件

之前博客里有一个非常类似的记录 使用 Builder 构造自定义构件,是在学习 Vala 语言时记录的。说的是如何使用 GtkBuilder 构造出自定义构件,仅仅只给出了一个示例代码。

最近在写 GTK+ 程序时被代码写 UI 的方式搞的很累,尽管那个 UI 并不复杂。在程序中有使用自定义的构件(是 GtkWidget 的子类),这里再重新整理一下关于 GtkBuilder 构造自定义构件的日志,并给出一个 C 语言版本的示例。

GTK+ 从 2.12 开始实现了 GtkBuildable 接口类与 GtkBuilder 类用于处理从 XML 格式的 UI 描述数据中构造出对象的实例,提高 GTK+ UI 程序的开发效率。只要实现了 GtkBuildable 接口类的 Widget 类,都可以在 XML 格式的 UI 描述数据中引用,并使用 GtkBuilder 进行实例化。

目前 GTK+ 库中基本上所有的 Widget 类都实现了 GtkBuildable 接口类,也就意味着可以使用 GtkBuilder 进行实例化。然而有的时间情况并不总是那么好,我们的项目需要自定义新的 Widget 类型完成特定的功能,幸运的是这些自定义的 Widget 类型只要实现了 GtkBuildable 接口类也是一样可以被 GtkBuilder 实例化的。

示例代码

示例中 src/hev-window.h 和 src/hev-window.c 是基于 GtkWindow 简单派生出来的 HevWindow 类。ui/main.xml 使用 HevWindow 类构造了一个 Window,在 src/hev-main.c 中创建了一个 GtkBuilder 实例 builder 来装载 ui/main.xml 并通过 name (对应描述文件中的 id)来获取构造出来的 HevWindow 类型的实例,再调用 gtk_widget_show 方法显示出来。

PS:编译参数中需要使用 gmodule-export-2.0,这样才能导出自定义类型与信号处理函数,需要描述文件中指定的信号回调将不能正常调用。具体见示例中的 Makefile

Over!

基于 GDBus 的 D-Bus 服务例程

从 GLib 2.30 开始,GIO 中高级 D-Bus 支持增加了 GDBusObjectManagerServer, GDBusObjectSkeleton, GDBusInterfaceSkeleton 等类,基于这些类实现 D-Bus 服务可以把 D-Bus 概念中的对象、接口(方法、属性和信号)与 GObject 的中的方法、属性和信号映射起来,使得开发过程中对象间接关系更直观。

GDBusObjectManagerServer 是管理 D-Bus Object 的服务器的类,它可以在给定的 GDBusConnection 上导出 GDBusObjectSkeleton 类型的对象。

GDBusObjectSkeleton 是 D-Bus Object 的骨架类,在开发过程中需要实现的 D-Bus Object 可以从此类派生出来,它可以增加很多的从此类 GDBusInterfaceSkeleton 派生出来的接口类。

GDBusInterfaceSkeleton 是用于导出 GDBusObjectSkeleton 类及子类的方法、属性和信号的接口类,每个实例只能增加到一个 GDBusObjectSkeleton 实例中,在我的实例中又抽象出了一个 HevDBusInterfaceSkeleton 类型,用于在合适的时机将 GDBusInterfaceSkeleton 增加到 GDBusObjectSkeleton 中,并连接信号。

示例代码

Over!

恶心的GObject[Part I][v0.1]

虽然GObject最初是为了简化C语言的OO开发而设立的,但毕竟C不是天生的OO语言,而其中有太多抽象的东西,反而让其难以入门…..

虽然以前花了很长时间去研究这东西,但限于当时的水平,一些东西并没有弄透彻,甚至有不少错误….

因为前段自己尝试用C语言来模拟OO,积累了不少经验,最近偶然又回顾了下GObject,一些似懂非懂的概念明朗了许多…..

Part I. GObject中最恶心的概念
下面罗列几个概念,都是难以弄明白的….

GCClosure,GClosure,GClosureMarshal,

GClosureNotifyData,GClosureNotify,SignalAccumulator,GSignalAccumulator

了吧,估计不少学了一段Gtk的人对上面的概念还是一知半解…

在具体展开前,我们需要知道GObject是怎么描述类,以及如何进行类型的判定和转换的….

我们知道C语言进行OO编程的时候要大量用到结构体函数指针,GObject也不例外,

而GObject中每一个类也是一到多个结构体[多个结构体是干嘛的后面会解释],GObject本身用多个int型变量来表示各个类型,类型的检测就是一个int型变量的检查,而类型的转换也是根据这个int型的变量[需要判断是否可以转换],进行结构体或指针的强制转换.

GCClosure,GClosure就是两个结构体!

前面的GCClosure多了个C,自然是C语言用的,不难想到GCClosure是GClosure一个封装,提供针对C语言的一种绑定,自然,GObject可以绑定到多种语言,比如Python,Ruby等等,目前绑定的比较好的是C[原生]/CPP/VALA/PYTHON

仔细看下

struct _GCClosure
{
  GClosure  closure;
  gpointer  callback;
};

注意,_GCClosure会用typedef在定义成GCClosure,这一点后面都类似,以后不再特殊说明,

通过GCClosure定义,不难发现其封装了一个GClosure的同时,还有一个callback,猜到了吧!GCClosure的主要功能就是回调!

估计不够淡定的朋友会觉得这也太扯淡了,回调的本质就是一个函数指针,包装这么多层干嘛?GObject这样设计自然有它的道理,

我们知道不同语言有不同的类型,比如C语言里就没有原生的String,

而即便是C语言我们仅在C语言,我们在定义回调函数时也可能要用到不同的形式,比如有的返回int,有的返回void,我们怎么区别这些呢?

不急,待我一步一步分析,
GOBject中GClosureMarshal是一个函数指针,但是要注意它是用来定义回调函数类型的而不是直接调用的!

typedef void  (*GClosureMarshal)    (GClosure   *closure,
                     GValue         *return_value,
                     guint           n_param_values,
                     const GValue   *param_values,
                     gpointer        invocation_hint,
                     gpointer    marshal_data);

看到它的参数没有,指定了回调返回值类型,回调函数参数的个数等等

而每一个GClosure都要有一个绑定的GClosureMarshal,

具体来看看

struct _GClosure
{
  /*< private >*/
  volatile          guint    ref_count : 15;
  volatile          guint    meta_marshal : 1;
  volatile          guint    n_guards : 1;
  volatile          guint    n_fnotifiers : 2;  /* finalization notifiers */
  volatile          guint    n_inotifiers : 8;  /* invalidation notifiers */
  volatile          guint    in_inotify : 1;
  volatile          guint    floating : 1;
  /*< protected >*/
  volatile          guint    derivative_flag : 1;
  /*< public >*/
  volatile          guint    in_marshal : 1;
  volatile          guint    is_invalid : 1;
 
  /*< private >*/ void   (*marshal)  (GClosure       *closure,
                        GValue /*out*/ *return_value,
                        guint           n_param_values,
                        const GValue   *param_values,
                        gpointer        invocation_hint,
                        gpointer        marshal_data);
  /*< protected >*/   gpointer data;
 
  /*< private >*/ GClosureNotifyData *notifiers;
 
};

注意,官方网上的Manual可能描述上过老,容易误导初学者,上面的源自GOjbect源码

罗列上面代码并不是大家一个一个分析的,只是要大家知道前面我说的一个Closure绑定一个Marshaller…..

我前面说过GClosureMarshal是用来定义回调函数类型的,不是用来调用的,GObject中真正的回调是marshal_data[够抽象的,这个是一个void *指针] ,关于这个我不多说什么[因为自己也没时间研究],因为一般不常用,主要用于其它语言间的绑定.

对于C语言,GObject本身已经提供了很多现成的C语言专用的Marshaller,下面给出一个最简单的C专用的Marshaller

/* VOID:VOID (./gmarshal.list:26) */
void
g_cclosure_marshal_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 void (*GMarshalFunc_VOID__VOID) (gpointer     data1,
                                           gpointer     data2);
  register GMarshalFunc_VOID__VOID callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;
 
  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_VOID__VOID) (marshal_data ? marshal_data : cc->callback);
 
  callback (data1,
            data2);
}

看到了吧那个callback可以指向marshal_data,而marshal_data也是一个函数指针,一般来说它应指向GCClosure中的callback,只是真正调用时是从GClosure中来调用而已.

而原始的GClosureMarshal本质上也是这样的……

注意VOID_VOID表示回调返回VOID,额外的参数为VOID[就是没有额外的参数]

好了,分析的差不多了,有了上面的基础我们就不难理解signal的链接与回调

对于C语言,

一个SIGNAL在CONNECT时,实际创建了一个C语言的Closure,并绑定了一个C语言的Marshaller,而C语言Marshaller的类型,要在SIGNAL 创建时来定义[注意常见的Gtk中g_signal_connect的常见SIGNAL都是已经定义好回调的类型的,所以你在定义已知信号的回调是要按类型定义],回调函数是在GCClosure中定义的,但本质上是通过GClosure来调用….

简单表示下

GCClosure{
  GClosure {
      ...
     其它很多和类型相关的东西;
      ...
      GClosureMarshal marshal; //这是个函数指针,
      //这个的参数中有一个要指向下面定义的callback
     ...
  }
 void *callback;
}

而我们在ui使用回调是将SIGNAL发送[EMIT]出去,若在signal队列中有绑定的HANDLER[就是我们的callback],就由其处理…

signal的发送非常复杂,这里不再赘述[如果要研究这个,可以从SignalNode结构入手]…..

我们仅需要知道[对于C语言]一旦找到了相应的SIGNAL的ID,就会找到指定GClosure[不是GCClosure],然后通过其找到GClosure并调用其中的marshall函数[传入的参数有何callback相关的],进而调用回调函数.

如果是C语言,可以调用类似g_cclosure_marshal_VOID__VOID的一个定义好的marshal,其内部会调用对应的指向callback的函数[具体参考上面那个C语言VOID_VOID型marshal,调用了传入marshal_data函数指针,也就是我们绑定到GCClosure的callback指针]

好了最扯淡的部分结束了,还有四个[GClosureNotifyData , GClosureNotify , SignalAccumulator , GSignalAccumulator]没说,不过这四个比较简单,理解起来也容易些,下面为了节约时间,一并说了.

GClosureNotifyData 是一个结构体,它包含了一个函数指针GClosureNotify

SignalAccumulator 也是一个结构体,他也包含了一个函数指针GSignalAccumulator

具体代码

struct _GClosureNotifyData
{
  gpointer       data;
  GClosureNotify notify;
};
 
typedef void  (*GClosureNotify)     (gpointer    data,
                     GClosure   *closure);
 
typedef struct
{
  GSignalAccumulator func;
  gpointer           data;
} SignalAccumulator;
 
typedef gboolean (*GSignalAccumulator)  (GSignalInvocationHint *ihint,
                     GValue            *return_accu,
                     const GValue          *handler_return,
                     gpointer               data);

上面的data即回调函数传入的参数

如果我们回调的参数中有定义在堆上的,并且调用完要释放,那么应该通过绑定一个GClosureNotify型的指针,而GSignalAccumulator指针则是用来控制某个信号响应后是否继续向下传递用的.

注意: GClosure中有指向GClosureNotifyData的指针,而SignalNode结构体中有个指向SignalAccumulator结构体的指针.

好了,本篇就到此为止,基本分析了GObject中最难理解的概念,因为写的仓促和个人水平有限希望发现问题的朋友能够给予指正.

Over!

From: http://pingf.is-programmer.com/posts/21355.html

函数指针、回调函数与 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 内核启动 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!