Common Lisp 的無限可能——GTK+ 3 與 CL 的快速圖形程序開發及簡單的 CL 介紹
Thu, 14 Feb 2013 20:28:51 +0800
章甲、虎頭
大家也許已經不止一次從 Lisp 的狂熱愛好者那裏聽說了「Lisp 真是世界第一語言」「Lisp 最 NB」之類的話。俺不想說那些沒用的玩意,今天就是上來發個小小的示例,GTK+3 和 Common Lisp 在一起是什麼模樣?
大家也許已經不止一次從 Lisp 的狂熱愛好者那裏聽說了「Lisp 真是世界第一語言」「Lisp 最 NB」之類的話。俺不想說那些沒用的玩意,今天就是上來發個小小的示例,GTK+3 和 Common Lisp 在一起是什麼模樣?
GNOME 不行其道也久矣,然為紅帽之屬也,何也?略熟 GNOME 軟件之關於窗必知予之旨意。
予好 BSD 許可而適 FreeBSD。予恨 GPL 許可而捨 Linux。然予因舊用 Emacs 及 GNOME,何也?蓋於予之心,無物可替之也。
是時,予仍 GNOME 之擁者也。大好而欲其更佳。
然世事難料,GNOME 3 之公布,大變也。
GNOME 3 之設優也,雖功能缺而設亦有不良者。是時予仍 Fedora 之用者,待之久矣。GNOME 3 驚予,乃喜歡之。予遂摒他山不顧。予念若假以時日,所缺必獲也。有微改,無見 gnome-about 之儔也。
後,予驚悉 GNOME 欲去 BSD 之援。予頗為呀驚,因 GNOME 嘗語其乃 UNIX 類系統之逍遙桌面於 gnome-about。是邪,GNOME 棄 gnome-about 久矣!安大事也!
數日,予適 FreeBSD。(早前亦試之而由無線網卡之不力,棄之)予頗愛此高整合之系統也。予嘗見彼行於 GNOME3,故未憂之。
然予試之再三,不能令行,考於網絡,未見結果。mezz@ 終斷然言,「何時合併入移植樹未知也」。予甚至報之於 GNOME 之 Bugzilla。然予終知,GNOME 不工移植。予一介軟件開發者也,甚重移植。RatDB 之一務即大善移植性也 [1]。KDE 有 FreeBSD 士團善其之功效。然,GNOME 安得未有乎!
此,吾未解也。
--------------
[1] 但言 POSIX 之系統,以譏 Windows 之可移植之執行檔。
若 GNOME 續行跋扈,予或去之。然予深喜 GNOME-Shell。予或自製之,必樂諸公。
予持些許時日於此怒論。若爾必問為何,但念若爾之所愛欺爾之感受,無人好之也,予亦然。
GNOME community is not a "community that makes great softwares" anymore, but a team belongs to Red Hat. Why do I say? Just look at your GNOME softwares' about dialog. It may give you much information.
I just moved to FreeBSD because I love BSD License. I hate the GPL license so I'd like to leave the Linux world. Looking more, I still use Emacs and GNOME because I really love them. No softwares can replace them in my heart.
At that time, I'm a GNOME fan. I really love it and I want it to be greater.
But things changed after GNOME 3's release.
GNOME 3 is a well-designed desktop though many features are not implemented and some designs are still bad. That time I'm still using Fedora and I'm waiting it for a very long time. GNOME 3 really give me a suprise and I fell in love with it. I've never looked at any other desktop environments because I believe GNOME is the best. I think give it more time, the lost features will be found! A small difference, I didn't find a thing like gnome-about.
Later, I heard that GNOME is planning to drop BSDs support. I feel very afraid and I couldn't believe what I'd seen, really. I hope GNOME not to do that because I still remember GNOME said "GNOME is a desktop environment for UNIX-like operating systems" in gnome-about. Oh, dear, GNOME has dropped gnome-about. So this is not an important decision.
Just, just a bit more later, I've moved to FreeBSD. (in fact, more earier I've tried but gived up because of unworkable wireless card) I enjoy using it, a great integrated operating system. I've seen somebody ran GNOME3 and GnomeShell so I'm not fearing about that.
And after trying, trying and trying, I found this unworkable so I searched around the network. And mezz@ finally said, "no idea when it'll be merged into ports tree". I even filed a bug on GNOME Bugzilla. But till then I believed, GNOME has no hard work about portability. As a software developer, I value the portability. And one of the RatDB's goals is "perfect portability" [1]. KDE even has a FreeBSD team for FreeBSD portability. But, why GNOME does NOT?
No. I've got no ideas.
[1] Only for POSIX systems. Why not Windows? Because the full name of Windows' PE is "Portable Executable". But it's not portable at all.
------------------
If GNOME continues to be overbearing, I may leave the GNOME world. However, I really love GNOME-Shell. One day I may create a new world, that's a new GNOME, but for PC, and human beings.
I spent some time on this post. If you're wondering why, just imagine what/who you love cheats you. Anybody would not love this feeling, including me.
我是一个大骗子,因为我又回来了。。哦不,准确说是被逼无奈啊。详情我就不说了,太伤心了。
首先下载 bcm Linux-STA 驱动。
make sudo make install insmod wl.ko depmod -a modprobe wl
好了
正式搬迁到 http://ekd123.org 。。。因为i11r (iS-PROGRAMMEr的缩写,也算吐槽一下这么长的名字) 在我这里访问实在不给力。
我个人并没有全看过,看过也不一定完全懂,有问题请去相关页面留言问原作者。最近自己买了个空间,欢迎常来逛逛,如果一个月后我还有资金维持那个独立博客,我可能会把这个blog里面我自认为可以保留的文都转走。
当你把以下文章完全看透,你就可以说你已经会 GObject 了(不仅仅是入门)。
Tiger Soldier 写的:
有点可惜的是第二篇无限延长了。。 话说这是 is-Programmer.com 上第一篇有关 GObject 的文章。
Tiger Soldier 是 OSD Lyrics 的作者。
pingf 写的:
文题虽稍显不雅,但内容很好,甚至是联系源代码的。有点可惜的是第二篇还是没出来。
pingf 是 OOC-GCC 的作者。
过眼云烟写的:
Garfileo 的 GObject 系列文章,因为他自己已经给出了目录,我就不多嘴了:
有点可惜的是没有 PDF 版离线阅读……
Garfileo 是尚未面世的 Cikada 的作者。
GNOME 官方的 GObject 手册。别人都说很老了,也很晦涩, Garfileo 说你能完全看懂里面的 Concepts 的话,就不用看他的文了。
《GObject对象系统》。很简单的入门文,不过似乎并不完全参照以上文所述的方法。
以下是 Ubuntu Tweak 作者 TualatriX 所翻译的官方手册:
10、GObject的消息系统:Closure(这个是叫闭包吧?)
开始学习吧!
#include <gio/gio.h>
#include <glib.h>
int
main (int argc,
char *argv[])
{
/* 少写了这个程序就崩溃了 调试了老半天 T_T */
g_type_init ();
GFile *file;/* 文件抽象数据类型 */
GOutputStream *fos; /* 用来写的 */
GError *error = NULL;
/* 创建file对象 */
file = g_file_new_for_path ("./test_file");
/* 获取输出流 */
fos = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
G_FILE_CREATE_NONE,
NULL, &error));
if (!fos)
{
g_error ("%s", error->message);
g_error_free (error);
return 1;
}
/* 写入 */
GString *str = g_string_new ("这些文字将写入 test_file。\n");
g_output_stream_write_all (fos, str->str, str->len, NULL, NULL, NULL);
g_output_stream_flush (fos, NULL, NULL);
g_string_free (str, TRUE);
/* 清理 */
g_output_stream_close (fos, NULL, NULL);
g_object_unref (file);
return 0;
}
#include <gio/gio.h>
#include <glib.h>
int
main (int argc,
char *argv[])
{
if (argc < 2)
return 1;
/* 少写了这个程序就崩溃了 调试了老半天 T_T */
g_type_init ();
GFile *file;/* 文件抽象数据类型 */
GInputStream *fis; /* 用来读的 */
GError *error = NULL;
GDataInputStream *dis; /* 抽象输入流 */
/* 创建file对象 */
file = g_file_new_for_path (argv[1]);
/* 获取输入流 */
fis = G_INPUT_STREAM (g_file_read (file, NULL, &error));
if (!fis)
{
g_error ("%s", error->message);
g_error_free (error);
return 1;
}
/* 读取 */
dis = g_data_input_stream_new (fis);
char *line;
while (TRUE)
{
line = g_data_input_stream_read_line (dis, NULL, NULL, &error);
if (error)
{
g_error ("%s", error->message);
return 1;
}
if (!line)
break;
g_print ("%s\n", line);
g_free (line);
}
/* 清理 */
g_input_stream_close (fis, NULL, NULL);
g_object_unref (file);
return 0;
}
效果
$ c89 -o reader reader.c `pkg-config --cflags --libs gio-2.0 glib-2.0` $ c89 -o writer writer.c `pkg-config --cflags --libs gio-2.0 glib-2.0` $ ./writer $ ./reader test_file 这些文字将写入 test_file。 $
该文基于我早前写 GKiu 时对 GNOME 密钥环的学习。
代码全部经过实测、优化,请放心阅读本文!
简单来说,就是存你密码的地方。假如吧,你在好多地方都办理了VIP卡,你总不能天天带在身上吧?而且还很麻烦,于是你就又买了一个保险柜,把你所有的VIP卡、钥匙、银行卡信用卡充值卡……都塞到这个保险柜里面了,你只需要一个密码就能打开找到自己需要的卡片钥匙。GNOME密钥环就是这么做的,它用一个密码锁住所有密码,于是你就不需要记住很多密码,只需要记住一个密码就全部可以了。需要放在正文里面强调的是强调的是,你用很多种不一样的密码正是因为安全原因,现在你正巧用了一个密码,所以骇客只需要获取你这一个密码你可就全线崩溃了,所以必须做好安全工作。当然,这是在本地存储的,所以要截获密码并不是那么容易。
Q:我的系统有 GNOME 密钥环吗?
A:一般来说,只要你的桌面环境是 GNOME 就有。当然如果你有 KDE,安装了一些使用了这个库的软件,也会有。
Q:我需要手动使用它吗?
A:完全不需要,这个东西是给程序用的,所以都会自动(在你的选项下,也许
)完成。
Q:如何调教密钥环?
A:呃。。。好吧,你可以用心灵感应遥控它,工具是强大到逆天的
seahorse,GNOME官方出品,质量有保证;还有出厂时候附赠的小刀一把,直拿软肋,名字就叫gnome-keyring,不过因为是小刀有些人不会用(警察也不会让你用的),所以不太受欢迎。
这就是我们要谈论的重点了!因为是首次接触 GNOME 密钥环(也许你是
),就算你以前用过 seahorse 也不一定用它的接口写过软件,所以就不会多做介绍给高级特性了!而且这部分也够绝大多数人用了,也许以后我还会多讲一些 GNOME 密钥环的 API。
因为我是 Fedora 对 Ubuntu/Debian 系的不太了解,于是只给出了 Fedora 的安装方法,Ubuntu/Debian 自己解决吧。
方法:
sudo yum install gnome-keyring-devel
在接下来的实例环节,我们要多次用到 seahorse 来验证我们的代码运行结果,所以也许你还需要安装一下 seahorse。
sudo yum install seahorse
注意 GNOME 密钥环的开发文件偏离了一般实践,它将包命名为 gnome-keyring-1(而不是1.0),因此你需要这样编译:
cc [文件名] `pkg-config --cflags --libs gnome-keyring-1 glib-2.0`
因为 GNOME 密钥环用到了 GLib,因此你也需要链接它。
现在就要开始学习使用GNOME密钥环库了!首先我们看看如何保存一个密码……
哇噢!这么长的参数列表!别着急,我们一个一个看,首先是这个 schema 参数。。。也许是的,GNOME 很多库都要这些东西,见怪不怪啦!长话短说,shema 就是一个定义,它定义了你这一个密钥环项目都保存了什么。那么。。莫非我们还要自己写一个 schema?!不瞒你说,GNOME库里面很多用schema的都非常复杂的定义!GNOME 密钥环也不例外,因此内部已经定义好了一个 schema,包含如下参数:
很显然这是一个用在网络上的 schema,而且令人一点也不意外的是,它这个名字就叫 GNOME_KEYRING_NETWORK_PASSWORD。
好了,我们已经干掉了前进路上的一个很大的障碍!继续前进!
keyring,这个很简单了,就是说要存到哪个密钥环里面嘛!不过问题是,这个类型怎么是 const gchar *?呃,其实每个密钥环是用名字标示的,比如你买了好几个一样的保险柜,要区分哪个是不是要贴上一张标签?那我们如何知道系统已有哪些密钥环呢?还要自己创建?还是枚举出一个最合适的已有的?不需要了!默认GNOME密钥环已经有了一个默认的,我们只需要在这里填上一个 NULL 就OK了,为了让代码更加可读(也更长),于是他们又定义了一个为NULL的GNOME_KEYRING_DEFAULT宏!
接下来的已经没什么比较需要讲解了,因为从名字就能看出来是什么意思。如果你细心的话。。你可能已经发现了一个后面带有 _sync 的同名函数,这是干什么的?嗯,其实默认情况下是异步运行的,也就是这个函数会立即返回并创建一个以回调函数为线程函数的线程,于是你就无须等待,这在一些需要大量存取的情况下非常有效,但是,我们一般的小程序只需要存一两次,为什么需要异步?那就用同步函数好了!也就是说,当操作完成后才会返回(当然,为了内容完备,我们把两个都讲解一下)。
但是你可能会疑惑,这个 destroy_data 又是做什么的呢?这就是另外一个需要的函数了,如果你传递了一个非 NULL 的 data(也就是GTK+里面的用户数据),那么函数会调用你指定的这个函数来释放你这个 data 的内存。
现在有了丰富的理论基础,我们先写一个异步版本的密钥环保存实例!
(在我这里似乎没有进入回调函数,这也可能成为我们使用同步版本的一个理由)
#include <stdlib.h>
#include <stdio.h>
#include <gnome-keyring.h>
static void save_cb (GnomeKeyringResult result,
gpointer data)
{
if (result != GNOME_KEYRING_RESULT_OK)
{
fprintf (stderr, "保存密码出错:%s!\n",
gnome_keyring_result_to_message (result));
}
else
{
printf ("保存密码成功!\n");
}
}
static void free_data_save_cb (gpointer data)
{
free (data);
}
void
save_password (const char *usr,
const char *pwd)
{
gpointer data = malloc (sizeof (100));
gnome_keyring_store_password (GNOME_KEYRING_NETWORK_PASSWORD,
GNOME_KEYRING_DEFAULT,
usr, /* display name */
pwd, /* password */
save_cb,
data, free_data_save_cb,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
save_password ("mike", "123456");
sleep (10); /* 必须sleep,否则程序退出就无法看到效果了 */
return 0;
}
在回调函数里,如果出现错误会使用 gnome_keyring_result_to_message 将结果转化为字符串的消息(如 strerror 所做一样)。
有必要强调一下,在 destroy_data 参数后面就是 schema 提供的参数了,你必须是一键一值,最后用 NULL 结尾,但我不明白的是,为什么需要这个,因为搜索查找的都是使用 display_name (也就是在 seahorse 中展示的名字)。确实是 seahorse 显示的名字,但是查找还需要这些 schema 中的参数。
可以看到,这个异步版本的非常复杂,而且要返回结果也是迂回的,所以一般实践还是喜欢用同步版本。因此在这里再给出一份存储密码的同步版本。
#include <stdio.h>
#include <gnome-keyring.h>
void
save_password (const char *usr,
const char *pwd)
{
GnomeKeyringResult r;
r=gnome_keyring_store_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
GNOME_KEYRING_DEFAULT,
usr, /* display name */
pwd, /* password */
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r != GNOME_KEYRING_RESULT_OK)
{
fprintf (stderr, "不能存储密码:%s!\n",
gnome_keyring_result_to_message (r));
}
else
{
printf ("存储密码成功!\n");
}
}
int main ()
{
save_password ("mike", "123456");
return 0;
}
很明显地,这个版本的代码缩减了许多,而且更易理解,当我按下回车键后,屏幕上显示出了我意料之中的喜讯。
关于运行效率,time可以告诉我们更多:
$ time ./a.out 存储密码成功! ./a.out 0.01s user 0.02s system 3% cpu 0.878 total
对于普通人来说,这点时间根本没有注意到就溜走了,所以并不成什么问题!
如果你真的要刨根问底,一睹真实效果的话,我只能有图有真相地给你答复:

(背景图为未来 Fedora 16 的背景图,个人非常喜欢)
“切,那不过是你用seahorse伪造的而已。”呃,你这么想就大错特错了,因为我还会使用 find_password 来找回这个密码!
还是按照惯例,先上异步版本,再上同步版本,为了代码简洁,我会忽略掉用户数据参数。因为 gnome_keyring_find_password 比存储密码的函数简单多了,所以就不会再解释参数意思了。
#include <stdio.h>
#include <gnome-keyring.h>
static void find_cb (GnomeKeyringResult result,
const char *string,
gpointer data)
{
if (result == GNOME_KEYRING_RESULT_OK)
{
printf ("找到了密码!密码是:%s\n", string);
}
else
{
fprintf (stderr, "查找密码失败:%s。\n",
gnome_keyring_result_to_message (result));
}
}
void find (const char *usr)
{
gnome_keyring_find_password (GNOME_KEYRING_NETWORK_PASSWORD,
find_cb,
NULL, NULL,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
find ("mike");
sleep (1);
return 0;
}
注意回调函数的第二个参数,是带有 const 的!这意味着你不能任意地 free 它。
看上去还是异常复杂,反对 store_password 的理由同样适用于这里,何不试试同步版本?不论从哪方面来说,同步版本都比异步版本易用(好吧,其实在任何方面,异步都比同步复杂;线程中的同步与这里的意思不同,而且它非常复杂)。
#include <stdio.h>
#include <gnome-keyring.h>
void find (const char *usr)
{
gchar *pwd;
GnomeKeyringResult r;
r=gnome_keyring_find_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
&pwd,
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r == GNOME_KEYRING_RESULT_OK)
{
printf ("找到了密码!密码是:%s\n", pwd);
gnome_keyring_free_password (pwd);
}
else
{
fprintf (stderr, "没有找到密码。。%s\n",
gnome_keyring_result_to_message (r));
}
}
int main ()
{
find ("mike");
return 0;
}
欢快的喜讯传来了,找到了密码!!哈哈,前面那个反对派呢?现在信了吧!不管你信不信,反正我信了!
如果你要删除一个密码,也是非常容易的,因为删除API是最简单的API!
看了例子就知道了!按惯例,异步在前。
#include <stdio.h>
#include <gnome-keyring.h>
static void del_cb (GnomeKeyringResult result,
gpointer data)
{
if (result == GNOME_KEYRING_RESULT_OK)
{
printf ("成功删除了密码!");
}
else
{
fprintf (stderr, "删除密码失败:%s!\n",
gnome_keyring_result_to_message (result));
}
}
void del (const char *usr)
{
gnome_keyring_delete_password (GNOME_KEYRING_NETWORK_PASSWORD,
del_cb,
NULL, NULL,
"user", usr,
"server", "http://ekd123.org",
NULL);
}
int main ()
{
del ("mike");
sleep (1);
return 0;
}
是比存储密码简单了许多!但是,对比了下面同步版本,就略显复杂了:
#include <stdio.h>
#include <gnome-keyring.h>
void del (const char *usr)
{
GnomeKeyringResult r;
r=gnome_keyring_delete_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
"user", usr,
"server", "http://ekd123.org",
NULL);
if (r == GNOME_KEYRING_RESULT_OK)
{
printf ("成功删除了密码!\n");
}
else
{
fprintf (stderr, "删除密码失败:%s\n",
gnome_keyring_result_to_message (r));
}
}
int main ()
{
del ("mike");
return 0;
}
看,这个才算简单的!有图有真相:

在此我还是向大家力荐使用同步版本,一般情况下同步版本带来的好处远大于弊端(除非你有特殊的环境),而且带来的延迟微不足道,调试起来也更加方便(会在完成后才返回)!
你可能觉得奇怪,为什么不用输入密码都能查找出来?那岂不是非常不安全?嗯,这个你大可放心,因为这是用你登录密码加密的,所以根本不用担心。但是如果你自己创建一个密钥环,那可就得需要密码啦!当然你可以让用户输入,也可以自己选定,一切由你决策!![]()
简单的说,Rawhide就是Fedora的滚动更新版,但这与 Gentoo、ArchLinux 等又不同,因为这个分支指向的是当前开发版(如同 FreeBSD 的 CURRENT 分支),所以极其不稳定。在更新前用你的脑子想想干什么?
需要注意的是,这不是测试版,是开发版,比如现在是 Fedora 15,进行中的版本是 Fedora 16,而 Rawhide 已经指向 Fedora 17 了。
某人说这是“最激进发行版的最激进分支”,窃以为非常贴切。
为 Fedora 开发者提供一个快速的反馈平台,所以一般都是开发者在用,普通用户一般用不到,当然如果你有开发经验而且有一定的解决问题能力可以尝试。
这是非常简单的,只需要
sudo yum install fedora-release-rawhide sudo yum update --disablerepo=\* --enablerepo=rawhide
即可。我强烈建议你不要用任何在X下的终端模拟器!因为我在使用的时候不知怎么 X 突然崩溃了,yum当然也崩溃掉了,因为它是一个模拟出来的终端。这样我就引来了好多麻烦的问题。更新途中突然崩溃的结果就是这个,要是恰巧包管理也坏掉了,那就囧了,所以一定要小心!
更新的时间会非常长,因为要更新电脑上所有的软件包,即使版本号一样,包名也不一样,比如 xxx.fc15 一定要更新到 xxx.fc17(没有17的话也一定会有16的)。建议你在睡前进行更新并关掉电源选项中的X分钟后睡眠、挂起,这会断网的。
在更新途中你可能会遇到各种各样的问题如“损坏依赖”“冲突”“未找到”等等,非常麻烦,一定要做好心理准备,当然一旦开始就没有什么问题了,除非突然给你说 404 错误。
那是因为 Rawhide 源默认指向一个列表,而如果你没有安装选择最快的源的插件,一般都非常慢,这里我们可以选用 163、网易等国内源来加速。不过这需要自己修改源。
打开 /etc/yum.repos.d/fedora-rawhide.repo,将所有baseurl前的#去掉,再改为你用的源的URL。附上一份我的fedora-rawhide.repo(163的,特点是更新快,不过速度比搜狐慢点,搜狐更新很慢)。
[rawhide] name=Fedora Rawhide - $basearch - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/rawhide/$basearch/os/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-rawhide&arch=$basearch enabled=1 metadata_expire=7d gpgcheck=0 [rawhide-debuginfo] name=Fedora Rawhide - $basearch - Debug - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/$basearch/debug/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-rawhide&arch=$basearch enabled=0 metadata_expire=7d [rawhide-source] name=Fedora Rawhide - Source - 163.com failovermethod=priority baseurl=http://mirrors.163.com/fedora/development/rawhide/source/SRPMS/ mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-source-$releasever&arch=$basearch enabled=0 metadata_expire=7d
注,该repo是我空写出来的,所以有一点小问题,建议你自己修改安装fedora-release-rawhide后出现的repo文件。
当然可以,而且比安装 Rawhide 更加方便,你只需要安装 preupgrade 即可,这是一个图形软件,很方便,唯一需要注意的是,你的 /boot 分区至少需要 500M 空余空间。
我这里遇到了一个很郁闷的问题,就是开机没法显示图形界面,这个问题很容易知道,显然是显卡问题。
这个问题也不是很难解决,只要用一个禁用调试的内核就解决了,你可以去bodhi和koji上搜索查找(描述里面会说 disable debug build),从Koji上下载下来,安装即可解决问题。注意千万不要升级内核,除非你找到了新的禁用调试的内核。