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

Posted on Sat, 18 Sep 2010 05:43:43 -1100

虽然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中最难理解的概念,因为写的仓促和个人水平有限希望发现问题的朋友能够给予指正.

我的邮件地址 pingf0@gmail.com

SlickEdit v15.0.1.3[目前最新版本] 破解方法

Posted on Sat, 07 Aug 2010 01:25:50 -1100

在此,还是要感谢下zhiwei.li那位大牛,虽然他的博客还未更新,

但是基本的方法还是一样的,

假设你用的是IDA PRO这个工具

那么首先要导入vs.exe

经前面提到的那位大牛的分析,vs.exe其中有个函数块只要返回0,就可正常使用SlickEdit了

 

仔细研究了下发现,这个exe文件在调用vsSubscription函数会调用一段代码,

而这段代码可以通过查询"|== NOT FOR RESALE ==|"字符串,这时会展现出一块比较大的图片

不难发现有两处可以返回(retn),图片中左路无需额外关注,重点在右边,只要把右路最后的一块代码(有关返回值的)顶部的

mov ax,*** (不同的版本可能有所不同)改为 xor ax,ax 就可以啦!

 

当然,如果你觉得上面的叙述很麻烦,那么直接看下面

对应最新的v15.0.1.3 的Windows版本(v15.0.0.X版本及更早的偏移地址有所不同,不通用),

       将vs.exe 偏移地址 0x0000EC90 处的  mov eax, ebp  改成  xor eax,eax

       对应的 16进制 的值就是  将8B C5 改成  33 C0

Linux版本也是大同小异,这两天没时间去弄,以后有时间再弄,或是等别人弄了.....

使用C语言来写网页

Posted on Mon, 12 Jul 2010 11:59:59 -1100

前面的几篇文章简单介绍了一些基础知识,

本文给出一个具体的实例,

先说明一下

测试环境是xampp套件(apache)+win32

开发工具是SlickEdit+Mingw

 

下面的文字主要介绍,用C语言[CGI]捕获html表单传递的信息.

其实和PHP,PERL类似,简单的方法就是环境变量的获取

[常用的环境变量,我前面的文章已经罗列了一些,这里就不多说了]

在C语言中,环境变量需要通过

	char *  getenv (const char *)

这个函数来获取,使用时需要包含stdlib.h这个头文件

有了这个函数,其它的就很简单了,写一个html的表单,比如

	<form name="input" action="../cgi-bin/test.exe" method="post">
Username: 
<input type="text" name="user" />
<input type="submit" value="Submit" />
</form>

注意 : action指向所需可执行文件[win下自然是exe了,linux下管用.cgi的后缀]

当然还有一个前提,就是Apache的CGI功能必须打开,这个配置很简单的,过阵子有空了再总结,这里不多罗嗦.

如果我们,输入了一些字符,点击提交后[假设那个test.exe里面用getenv捕获了某个环境变量,并打印出来],

会发现和我们想象的有些差异,

这里简单说明一下,

        1.最常见的疑问是html中的转义字符串,命令行中的\n或是\n\r到html里就应该用</br>,类似的还有一些

        2.如果我们输入的有空格,那么返回时编程了'+'号

        3.如果我们输入一个中文词组[假定GB2132编码]--"你好",那么返回的是 "%C4%E3%BA%C3"

        4.其它没有总结的......(哈哈)

1的话自己注意下就好,2和3可以写一个简单的函数避免这些问题,比如像下面这样

void   decode(unsigned char *src , unsigned char *dst)
{
	unsigned int len=0;
	unsigned int i=0,j=0;

	len=strlen(src);
	while (i<len) {
		if (*(src+i) == '+') {
			*(dst+j) = ' ';
		} else if (*(src+i) == '%') {
			unsigned int code;    
			if (sscanf(src+i+1,"%2x",(unsigned int *)&code)!=1) {
				code=(unsigned int)'?';
			}
			*(dst+j)=(unsigned char)code;
			i+=2; 
		} else{
			*(dst+j)=*(src+i);
		}
		++i;
		++j;
	} 
	*(dst+j)='\0';  
}    

啰嗦的就到这里,下面给出完整的测试用例[关于环境变量的意义,见我前文的总结]

#include<stdio.h>
#include<stdlib.h>    
 
void   decode(unsigned char *src , unsigned char *dst)
{
	unsigned int len=0;
	unsigned int i=0,j=0;

	len=strlen(src);
	while (i<len) {
		if (*(src+i) == '+') {
			*(dst+j) = ' ';
		} else if (*(src+i) == '%') {
			unsigned int code;    
			if (sscanf(src+i+1,"%2x",(unsigned int *)&code)!=1) {
				code=(unsigned int)'?';
			}
			*(dst+j)=(unsigned char)code;
			i+=2; 
		} else{
			*(dst+j)=*(src+i);
		}
		++i;
		++j;
	} 
	*(dst+j)='\0';  
}    



int   main()    
{    
	unsigned char *env;

	long len;

	printf("%s\n\n", "Content-Type:text/html;charset=gb2312");


	env=getenv("CONTENT_LENGTH");
	printf("CONTENT_LENGTH : %s</br>",env);

	env=getenv("DOCUMENT_ROOT");
	printf("DOCUMENT_ROOT : %s</br>",env);

	env=getenv("HTTP_REFERER");
	printf("HTTP_REFERER : %s</br>",env);

	env=getenv("HTTP_USER_AGENT");
	printf("HTTP_USER_AGENT : %s</br>",env);

	env=getenv("PATH_INFO");
	printf("PATH_INFO : %s</br>",env);

	env=getenv("PATH_TRANSLATED");
	printf("PATH_TRANSLATED : %s</br>",env);

	env=getenv("QUERY_STRING");
	printf("QUERY_STRING : %s</br>",env);

	env=getenv("REMOTE_ADDR");
	printf("REMOTE_ADDR : %s</br>",env);

	env=getenv("REMOTE_ADDR");
	printf("REMOTE_ADDR : %s</br>",env);

	env=getenv("REMOTE_HOST");
	printf("REMOTE_HOST : %s</br>",env);

	env=getenv("REQUEST_METHOD");
	printf("REQUEST_METHOD : %s</br>",env);

	env=getenv("SCRIPT_NAME");
	printf("SCRIPT_NAME : %s</br>",env);

	env=getenv("REQUEST_URI");
	printf("REQUEST_URI : %s</br>",env);

	env=getenv("SERVER_NAME");
	printf("SERVER_NAME : %s</br>",env);

	env=getenv("SERVER_PORT");
	printf("SERVER_PORT : %s</br>",env);

	unsigned char oriData[500];
	unsigned char data[500];
	fgets(oriData,500,stdin);
	printf("original data : %s , %d</br>",oriData,strlen(oriData)  );
	decode(oriData,data);
	printf("data : %s , %d</br>",data,strlen(data)  );

	return   0;    
}    

对应的html表单的代码前面贴过了,就不重复贴了,

假设我们在表单中填上"你好"这个词组,那么当我们点击提交按钮后,

如果配置正确的话,浏览器会像我们返回类似如下的字串,

CONTENT_LENGTH : 17
DOCUMENT_ROOT : E:/xampp/htdocs
HTTP_REFERER : http://localhost/test.html
HTTP_USER_AGENT : Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6
PATH_INFO : (null)
PATH_TRANSLATED : (null)
QUERY_STRING :
REMOTE_ADDR : 127.0.0.1
REMOTE_ADDR : 127.0.0.1
REMOTE_HOST : (null)
REQUEST_METHOD : POST
SCRIPT_NAME : /cgi-bin/test2.exe
REQUEST_URI : /cgi-bin/test2.exe
SERVER_NAME : localhost
SERVER_PORT : 80
original data : user=%C4%E3%BA%C3 , 17
data : user=你好 , 9

HTTP报文实例

Posted on Mon, 12 Jul 2010 00:43:03 -1100

本文整理自网络,原始链接不知何处........

 

在罗列具体的实例之前,先啰嗦几句概念性的东西.

GET,POST,SOAP都是基于HTTP协议的,但是,

POST 是被设计用来向上放东西的,而GET是被设计用来从服务器取东西的,

GET也能够向服务器传送较少的数据,而Get之所以也能传送数据,

只是用来设计告诉 服务器,你到底需要什么样的数据.

POST的信息作为HTTP 请求的内容,而GET是在HTTP 头部传输的.

当然数据传送量上,POST自然要多些...

SOAP是依赖于HTTP POST模式实现的,它遵循一种特殊的xml消息格式
在SOAP中,Content-type设置为: text/xml,任何数据都可以xml化

具体看以下HTTP的格式

发送

<request line>

<headers>

<blank line>

[<request-body>]

接收

<status line>

<headers>

<blank line>

[<response-body>]

在响应中唯一真正的区别在于第一行中用状态信息代替了请求信息。状态行(status line)通过提供一个状态码来说明所请求的资源情况。

而常用的状态码如下

◆200 (OK): 找到了该资源,并且一切正常。
◆304 (NOT MODIFIED): 该资源在上次请求之后没有任何修改。这通常用于浏览器的缓存机制。
◆401 (UNAUTHORIZED): 客户端无权访问该资源。这通常会使得浏览器要求用户输入用户名和密码,以登录到服务器。
◆403 (FORBIDDEN): 客户端未能获得授权。这通常是在401之后输入了不正确的用户名或密码。
◆404 (NOT FOUND): 在指定的位置不存在所申请的资源。

 

 

下面仅是HTTP中最常用的GET和POST方法几个实例,以及基于POST的SOAP方法

GET 方法

发送:

GET /DEMOWebServices2.8/Service.asmx/CancelOrder?UserID=string&PWD=string&OrderConfirmation=string HTTP/1.1
Host: api.efxnow.com

接收:

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<objPlaceOrderResponse xmlns="https://api.efxnow.com/webservices2.3">
<Success>boolean</Success>
<ErrorDescription>string</ErrorDescription>
<ErrorNumber>int</ErrorNumber>
<CustomerOrderReference>long</CustomerOrderReference>
<OrderConfirmation>string</OrderConfirmation>
<CustomerDealRef>string</CustomerDealRef>
</objPlaceOrderResponse>

注意其中没有实质的内容,自然也没有CONTENT-LENGTH,所以在CGI中若是GET方法发送的报文,通过环境变量获得的值为NULL

特别要注意其中的空行,之前是报文头,之后是报文的实际内容,下面的POST方法,发送时也有类似的规则

POST方法

发送

POST /DEMOWebServices2.8/Service.asmx/CancelOrder HTTP/1.1
Host: api.efxnow.com
Content-Type: application/x-www-form-urlencoded
Content-Length: length

UserID=string&PWD=string&OrderConfirmation=string

接收

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<objPlaceOrderResponse xmlns="https://api.efxnow.com/webservices2.3">
<Success>boolean</Success>
<ErrorDescription>string</ErrorDescription>
<ErrorNumber>int</ErrorNumber>
<CustomerOrderReference>long</CustomerOrderReference>
<OrderConfirmation>string</OrderConfirmation>
<CustomerDealRef>string</CustomerDealRef>
</objPlaceOrderResponse>

最后再介绍一种基于POST的方法

SOAP方法(soap 1.2)

发送

POST /DEMOWebServices2.8/Service.asmx HTTP/1.1
Host: api.efxnow.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
    <CancelOrder xmlns="https://api.efxnow.com/webservices2.3">
      <UserID>string</UserID>
      <PWD>string</PWD>
      <OrderConfirmation>string</OrderConfirmation>
    </CancelOrder>
</soap12:Body>
</soap12:Envelope>

回复

HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
    <CancelOrderResponse xmlns="https://api.efxnow.com/webservices2.3">
      <CancelOrderResult>
        <Success>boolean</Success>
        <ErrorDescription>string</ErrorDescription>
        <ErrorNumber>int</ErrorNumber>
        <CustomerOrderReference>long</CustomerOrderReference>
        <OrderConfirmation>string</OrderConfirmation>
        <CustomerDealRef>string</CustomerDealRef>
      </CancelOrderResult>
    </CancelOrderResponse>
</soap12:Body>
</soap12:Envelope>          

 

另外要补充说明的是,上面例子中报文头部分都比较简单,还有一些特别的属性可以根据需要添加,

比如要求服务器自动断开链接等功能就可以通过一些属性来设置

再举两个例子

GET发送

GET /books/?name=Professional%20Ajax HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive

POST发送

POST / HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
     (----此处空一行----)
name=Professional%20Ajax&publisher=Wiley

ubuntu 10.04 LTS 初体验

Posted on Mon, 03 May 2010 12:30:41 -1100

哈哈,笨兔新的lts版终于出来了,五一期间体验了把,感觉还不错,其实自从8.04后感觉一直都不错........

1.悲剧的wubi安装,

这次的安装还是采用了最最SB的方式,wubi,但是安装起来却费了一番时间,主要是开始装了下,因为种种原因又卸载了,

然后grub就认不到再次安装的笨兔了........

只能从网上搜罗一堆关于grub的信息,自己想法子修复了[真没想到自己这次用wubi安装还这么悲剧]

修复后的grub应该有类似下面的文字

menuentry "unbuntu 10.04 LTS (on /dev/sda8)" {
 insmod ntfs
 set root='(hd0,8)'
 loopback loop0 /ubuntu/disks/root.disk
 set root='(loop0)'
 linux /boot/vmlinuz-2.6.32-21-generic root=/dev/sda8 loop=/ubuntu/disks/root.disk ro
 initrd /boot/initrd.img-2.6.32-21-generic
 boot
}

而一开始进不去的时候就在命令行把上面的东西输入一遍就行了,

进去后在把类似的文字加在grub.cfg文件中或者用简单的方法update-grub2然后reboot,当然你也可以去修改menu.lst

2.悲剧的无线网卡

broadcom的网卡默认驱动会有问题,能识别到,但是搜不到无线网络......

关键是我没有额外的网线了,再准确点说上网的密码也记不得了,只有一个tplink.............

悲剧的又要向别人借网线把那个bmwcl的kernel包给装上......

3.悲剧的intel集成显卡

原以为从9.10后intel的烂显卡会好一点,但是实际上并不是如此,虽然默认的驱动特效能打开了

但是用clutter这类库的时候总会有这样那样的问题,记得9.10上也是如此,这方面n卡的驱动做的不错,效果好安装也容易,

于是编译安装了最新版的intel的2d驱动,最新版的mesa.....

还是有问题........

偶然间把驱动给屏蔽掉,发现分辨率并没变小,只是特效开不了而已,但是clutter库的动画效果运行正常了......

这也太悲剧了点........

不过按照英特尔官方的站点有提供clutter和cairo的下载,感觉应该是能够支持的啊.......

唉,现在还没找到解决问题的办法,下次买本打死不买英特尔的集显了.........