分类: LINUX
2010-05-09 11:25:59
在glib循环中动态加入dbus的支持
需求:
有时候需要在gtk_main执行以后,加入一些回调函数,希望能在主循环运行过程中被执行,常见的例子是一些插件的运行;
实现原理:
GLIB的执行原理本质上是类似于select的,只不过,select的单位不是fd而是一个称为GSource的结构,可以简单的理解为,它是对一个fd的封装,里面还包含了一些回调函数及其它,这些回调函数分为,glib本身的循环所需要的,也包括我们自己指定于这个fd对应的回调函数;glib循环本身所需要的回调函数至少包括prepare,check,dispatch,它先调用prepare,如果你需要做什么准备工作就在这里面做,如果不需要直接返回false,然后是check,这里比较重要,如果你需要你的dispatch函数得到处理,那么需要返回true,而dispatch函数在check返回true以后就会被执行,在那里你可以调用你先前设置好回调函数;
实现步骤:
1, 创建一个GSourceFuncs,如下
GSourceFuncs test_source_funcs =
{
test_source_prepare,
test_source_check,
test_source_dispatch,
NULL
};
其中函数内容为:
static gboolean test_source_prepare (GSource *source,
gint *timeout)
{
return FALSE;
}
static gboolean test_source_check (GSource *source)
{
if (!test_poll_fd.revents & G_IO_IN)
{
return FALSE;
}
/*something else to check*/
return TRUE;
}
static gboolean test_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
if (callback)
{
return callback(user_data);
}
return TRUE;
}
2, 根据上面的结构创建一个GSource;
GSource *source = g_source_new (&test_source_funcs, sizeof(GSource));
3, 创建文明需要的事件观察GPollFD;
这里需要先open一些fd,或者通过dbus_connection_get_unix_fd(dbus_connection, &fd)取得fd,然后通过:
test_poll_fd.fd = fd;
test_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
构造GPollFD;
4, 加入到glib主循环
g_source_add_poll (source, &test_poll_fd);
g_source_set_callback(source, test_read_callback, NULL, NULL);
g_source_attach(source, NULL);
下面是释放这个source:
g_source_unref (source);
这个test_read_callback就是我们最关心的回调函数,比如对于dbus的fd检测,我们就可以在这个回调函数里面执行dispatch,比如:
do
{
dbus_connection_read_write_dispatch(dbus_connection,
0);
}
while (dbus_connection_get_dispatch_status(dbus_connection) ==
DBUS_DISPATCH_DATA_REMAINS);
具体细节,请参阅“沒有glib的dbus使用”;
完整的代码如下:
cat buttons.c
#include
#include
#define WYLHISTORY_TEST
/* Create a new hbox with an image and a label packed into it
* and return the box. */
static GtkWidget *xpm_label_box( gchar *xpm_filename,
gchar *label_text )
{
GtkWidget *box;
GtkWidget *label;
GtkWidget *image;
/* Create box for image and label */
box = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (box), 2);
/* Now on to the image stuff */
image = gtk_image_new_from_file (xpm_filename);
/* Create a label for the button */
label = gtk_label_new (label_text);
/* Pack the image and label into the box */
gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);
gtk_widget_show (image);
gtk_widget_show (label);
return box;
}
#ifdef WYLHISTORY_TEST
static GPollFD test_poll_fd;
static gboolean test_source_prepare (GSource *source,
gint *timeout)
{
return FALSE;
}
static gboolean test_source_check (GSource *source)
{
if (!test_poll_fd.revents & G_IO_IN)
{
return FALSE;
}
/*something else to check*/
return TRUE;
}
static gboolean test_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
if (callback)
{
return callback(user_data);
}
return TRUE;
}
GSourceFuncs test_source_funcs =
{
test_source_prepare,
test_source_check,
test_source_dispatch,
NULL
};
#include
#include
#include
#include
#define TEST_SERVICE "com.woojoy.test_dbus"
#define TEST_PATH "/com/woojoy/test_dbus"
#define MATCH_RULE "type='signal'"
DBusConnection *dbus_connection = NULL;
/*below is the signal filter to receive the others signal,ofcourse you should change the MATCH_RULE,so filter the signals you donot care*/
static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *data)
{
const char *interface = dbus_message_get_interface(message);
printf("in test_dbus the interface is %s \n",interface);
const char *member= dbus_message_get_member(message);
printf("in test_dbus the member is %s \n",member);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/*below is the message handler,here you should accept the others remote method invoke I just printf some info*/
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
printf("this is test_dbus message_handler\n");
return (DBUS_HANDLER_RESULT_HANDLED);
}
/*here is to register to dbus server,so that it can send the info to us*/
static void
connect_hook(DBusConnection *connection, void *data)
{
DBusError error;
DBusObjectPathVTable vtable = { .message_function = message_handler, };
dbus_error_init(&error);
if (!dbus_bus_request_name(connection, TEST_SERVICE, 0, &error)) {
printf("dbus_bus_request_name error\n");
return;
}
dbus_bus_add_match(connection, MATCH_RULE, &error);
if (dbus_error_is_set(&error)) {
printf("after dbus_bus_add_match error\n");
return;
}
if (!dbus_connection_register_object_path(connection, TEST_PATH, &vtable, NULL)) {
printf("dbus_connection_register_object_path error\n");
return;
}
dbus_error_free(&error);
return;
}
int get_dbus_fd()
{
printf("enter get_dbus_fd\n");
DBusError derror;
dbus_error_init(&derror);
dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &derror);
if (dbus_connection == NULL) {
fprintf(stderr,"System DBus connection failed: %s", derror.message);
dbus_error_free(&derror);
return -1;
}
/*above is to get the dbusconnection,which actually is a socket fd*/
connect_hook(dbus_connection,NULL);
/*above is to register to dbus server*/
if (!dbus_connection_add_filter(dbus_connection, message_filter, NULL, NULL)) {
printf("after dbus_connection_add_filter error\n");
return -1;
}
/*here is add some filter which will get the message before message_handler*/
int fd=-1;
dbus_connection_get_unix_fd(dbus_connection, &fd);
printf("the fd %d\n",fd);
return fd;
}
static gboolean test_read_callback(gpointer user_data)
{
do {
dbus_connection_read_write_dispatch(dbus_connection, 0);
} while (dbus_connection_get_dispatch_status(dbus_connection) == DBUS_DISPATCH_DATA_REMAINS);
return TRUE;
}
void setup_dbus()
{
printf("enter setup_dbus\n");
int fd=get_dbus_fd();
printf("after get_dbus_fd fd %d\n",fd);
if(fd < 0){
printf("get_dbus_fd error\n");
return;
}
GSource *source = g_source_new (&test_source_funcs, sizeof(GSource));
test_poll_fd.fd = fd;
test_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
g_source_add_poll (source, &test_poll_fd);
g_source_set_callback(source, test_read_callback, NULL, NULL);
g_source_attach(source, NULL);
g_source_unref (source);
printf("exit setup_dbus\n");
}
#endif
/* Our usual callback function */
static void callback( GtkWidget *widget,
gpointer data )
{
g_print ("Hello again - %s was pressed\n", (char *) data);
#ifdef WYLHISTORY_TEST
static int initialed=FALSE;
if(!initialed){
initialed = TRUE;
setup_dbus();
}
#endif
}
int main( int argc,
char *argv[] )
{
/* GtkWidget is the storage type for widgets */
GtkWidget *window;
GtkWidget *button;
GtkWidget *box;
gtk_init (&argc, &argv);
/* Create a new window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
/* It's a good idea to do this for all windows. */
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (gtk_main_quit), NULL);
/* Sets the border width of the window. */
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
/* Create a new button */
button = gtk_button_new ();
/* Connect the "clicked" signal of the button to our callback */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "cool button");
/* This calls our box creating function */
box = xpm_label_box ("info.xpm", "cool button");
/* Pack and show all our widgets */
gtk_widget_show (box);
gtk_container_add (GTK_CONTAINER (button), box);
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (window), button);
gtk_widget_show (window);
/* Rest in gtk_main and wait for the fun to begin! */
gtk_main ();
return 0;
}
Cat Makefile
CC = gcc
CFLAGS = -Wall -Wunused \
-DG_DISABLE_DEPRECATED \
-DGDK_DISABLE_DEPRECATED \
-DGDK_PIXBUF_DISABLE_DEPRECATED \
-DGTK_DISABLE_DEPRECATED
buttons: buttons.c
$(CC) buttons.c -o buttons $(CFLAGS) `pkg-config gtk+-2.0 dbus-1 --cflags --libs`
clean:
rm -f *.o buttons
测试:
运行 ./buttons
这时候不点button,那么通过dbus-send发送任何system的消息,是不会有什么打印信息的;
如果运行以后,点了UI上的button,那么再通过dbus-send发送system的信号的时候,就会看到相关的打印信息了;
如下:
dbus-send --system --type=signal /org/openmoko/PhoneKit/Network org.openmoko.PhoneKit.Network.signal_changed
int32:12
in
test_dbus the interface is org.freedesktop.DBus
in
test_dbus the member is NameOwnerChanged
in
test_dbus the interface is org.openmoko.PhoneKit.Network
in test_dbus the member is signal_changed
That is OK!
备注:
作者:wylhistory
联系方式:wylhistory@gmail.com
chinaunix网友2010-12-15 11:26:59
很好啊,有个问题请教下你。 当client端异常退出,server端用dbus什么消息能够检测到呢? 我的邮箱是f21@live.cn,望你不吝指教.