先说一下用的是debian 6,evince版本是2.30.3。

去年在evince的邮件列表中问过这个问题,得到的回答是人手不够,并且这个功能不是太紧急,因此目前并不考虑实现。随着看pdf的时间越来越长,白色背景看着实在很不爽,但是又不懂gtk编程,因此想通过修改反色的行为来实现修改背景颜色的功能,即当选择了反色的时候并不是把背景和字体的颜色都反过来,而是把背景颜色修改为我喜欢的颜色,字体的颜色不变。

先到源里把代码下下来,grep一下关键字”invert”加上人肉筛选加上gdb打断点后发现实现反色的功能在

libdocument/ev-document-misc.c
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
void
ev_document_misc_invert_surface (cairo_surface_t *surface) {
#if CAIRO_VERSION > CAIRO_VERSION_ENCODE(1, 9, 2)
	cairo_t *cr;
 
	cr = cairo_create (surface);
 
	/* white + DIFFERENCE -> invert */
	cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
	cairo_set_source_rgb (cr, 1., 1., 1.);
	cairo_paint(cr);
	cairo_destroy (cr);
#else
	guchar *data;
	gint    rowstride;
	gint    width, height;
	gint    x, y;
 
	data = cairo_image_surface_get_data (surface);
	rowstride = cairo_image_surface_get_stride (surface);
	width = cairo_image_surface_get_width (surface);
	height = cairo_image_surface_get_height (surface);
 
	for (y = 0; y < height; y++) {
		guchar *p = data + y * rowstride;
 
		for (x = 0; x < width; x++) {
			p[0] = 255 - p[0];
			p[1] = 255 - p[1];
			p[2] = 255 - p[2];
			p += 4;
		}
	}
 
	cairo_surface_mark_dirty (surface);
#endif
}

经过printf确定走的是#else分支。简单的反色功能没有区分背景和字体的颜色,只是把每个点的rgb颜色给反过来(第327-329行,p[0],p[1],p[2]分别表示rgb的值)。因为背景颜色大多是白色(或接近白色),所以把第327-329行改为

if ((245 <= p[0] && p[0] <= 255) &&
    (245 <= p[1] && p[1] <= 255) &&
    (245 <= p[2] && p[2] <= 255)) {
    p[0] = 204; /* cc */
    p[1] = 232; /* e8 */
    p[2] = 207; /* cf */
}

这样凡是rgb值落在[245, 255]中的颜色都会变成#cce8cf,不管是背景颜色还是字体颜色。误伤是难免的,而且字体边沿还是有些白色的印迹去不掉,不过凑合着用是没问题了。然后重新编译(根据自己的需要修改选项,如果依赖不满足的话对照configure的输出一个个装就是了):

$ ./configure --prefix=/usr --enable-nls --disable-scrollkeeper --disable-dbus --disable-debug --disable-tests --disable-nautilus --disable-thumbnailer --disable-previewer --disable-comics --without-keyring --without-gconf --without-gtk-unix-print
$ make

编译完后不要急着make install,这样会覆盖原来的版本。把原来的/usr/lib/libevdocument.so.2.0.0备份一下,用libdocument/.libs目录下新生成的libevdocument.so.2.0.0替换掉即可,这样每次勾选了“反色”之后背景就变成淡绿色了。不过取消选择之后并不会立即变回白色,而是需要改变一下pdf的大小重新刷新一下。

另外有一个修改pdf的内容以达到更改颜色的方法,原文在这里

evince直到现在的gnome3.4还没有更改背景颜色的功能,因为这个问题我都有一种冲动要换到kde了,不说okular,就是kde3时代的kpdf都比现在的evince好用。但是在kde下的终端不给力,bbs的ascii图根本没法看(vte-based的就不会,但是使用vte的终端基本都是gtk写的,而gtk程序在kde下的外观惨不忍睹),而且旧的图标主题还不能直接用(我最喜欢的tango就不能直接用在kde4上,可能是有些图标的名称改变了,导致图标和功能错位)。最后还是妥协了,继续留在gnome——准确点说是gnome2,接受被抛弃的命运。

发表于 2012年5月5日

unix有一句口号“一切都是文件”,说的是普通文件,外设甚至网络都可以当成一个文件来看待:从磁盘上的文件中读取数据,从鼠标/键盘等外设获得输入,接收网络信息等就是从“文件”中读取数据;把数据保存到文件,将文字显示在屏幕上,播放音乐和电影,发送网络信息等可以认为向“文件”写入数据。这个高度统一的接口为操作提供了巨大的便利,用户不需知道实际的对象是什么,只使用系统提供的read/write接口就能完成几乎所有的io操作。

既然所有的设备都能被抽象成文件并且用统一的接口进行操作,而且个人觉得在操作系统的几个组成部分(内存管理,进程调度等)中文件系统是相对简单的部分,从这个统一的接口开始了解其内部工作机制或许是个不错的选择。

虚拟文件系统(Virtual Filesystem Switch)

早在1985年sun发表了一篇论文(参考资料[1]),提出了一个用于Sun OS的文件系统接口。论文开头说明了设计的目的:

o   将与文件系统实现相关的部分和与实现无关的部分分开,两者之间由一个经过良好设计的接口连接
o   这些接口需要支持unix文件系统的操作原语(如创建/删除文件和目录等操作),并且支持各种各样的文件系统
o   接口要满足远程文件系统对于client的请求操作(例如一些连接/断开的操作,对应于open/close)
o   接口提供的操作都是原子性的,锁操作留给具体实现

整个接口所在的位置和提供的功能如下:

                            +--------------+
                            | System Calls |
                            +--------------+
                                   |
                            +--------------+
                            | Vnode Layer  |
                            +--------------+
                                   |
        /--------------------------+----------------------------\
        |                          |                            |
+----------------+      +---------------------+       +------------------+
| PC File System |      | 4.2 BSD File System |       | NFS | NFS Server |
+----------------+      +---------------------+       +------------------+

阅读全文…

发表于 2012年4月26日

这是大三小学期的时候做的,不知不觉快3年了,转过来留个纪念。

阅读全文…

发表于 2012年4月25日

第25章。

lua的一个重要的应用是作为程序配置文件的语言(configuration language)。下面通过一些例子循序渐进地说明其应用。

一个简单的例子

假设一个c语言程序要画一个窗口界面,窗口的长和宽可以由用户指定。要实现这个功能还有另外更简单的方法,例如使用环境变量或只包含(key, value)形式的普通文本文件,但是如果使用普通文本文件你还是需要对其进行解析。程序的配置文件如下:

1
2
3
4
-- conf.lua
-- define window size
width = 200
height = 300

下面的程序演示了怎样使用lua提供的接口来获得这两个变量的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio .h>
 
#include <lua .h>
#include <lauxlib .h>
#include <lualib .h>
 
int main(void)
{
    lua_State* l;
 
    l = luaL_newstate();
    luaL_openlibs(l);
 
    if (luaL_loadfile(l, "./conf.lua") != 0) {
        fprintf(stderr, "luaL_loadfile err: %s.\n", lua_tostring(l, -1));
        goto end;
    }
 
    if (lua_pcall(l, 0, 0, 0) != 0) {
        fprintf(stderr, "lua_pcall err: %s.\n", lua_tostring(l, -1));
        goto end;
    }
 
    lua_getglobal(l, "width");
    if (!lua_isnumber(l, -1))
        fprintf(stderr, "'width' should be a number.\n");
    else
        printf("width = %d\n", lua_tointeger(l, -1));
    lua_pop(l, 1);
 
    lua_getglobal(l, "height");
    if (!lua_isnumber(l, -1))
        fprintf(stderr, "'height' should be a number.\n");
    else
        printf("height = %d\n", lua_tointeger(l, -1));
    lua_pop(l, 1);
 
end:
    lua_close(l);
    return 0;
}
</lualib></lauxlib></lua></stdio>

阅读全文…

发表于 2012年4月14日

封装

c语言使用的struct没有访问控制,任何程序都能访问struct的成员。为了隐藏struct里的成员名称和位置,可以将具体的struct定义放在.c文件中,而在头文件中增加一个指向该结构体的指针。因为无法得知指针类型的具体定义,对这个私有结构体成员的访问只能通过提供的set()和get()函数。

下面是头文件test.h的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __TEST_H__
#define __TEST_H__
 
struct test {
    struct _test* t;
};
 
int test_constructor(struct test*);
int test_get(struct test*);
void test_set(struct test*, int);
void test_destructor(struct test*);
 
#endif

对外提供的结构体信息只有一个指向struct _test的指针,由于struct _test在.c文件中定义,其它程序无法知道它的具体定义。下面是实现具体功能的test.c:

阅读全文…

发表于 2012年4月3日

中断了的lua学习再次回来了……之前学完基本语法后就不想看接下来的标准库讲解了,因为看了不用基本等于白看,没几天就忘了;但是不看的话连lua有什么功能都不知道,更不用说知道把lua用在什么地方。最近想学习用lua来扩展程序,因此又捡起来了,不过跳过了书中的part II和part III,这里是第24章。顺便说一下,这里使用的lua版本也从5.1.4升到5.2.0,不过区别应该不大。

lua是一个嵌入式语言,就是说它不是一个单独的程序,而是一套可以在其它语言中使用的库,在前面使用过的lua交互程序其实是利用lua提供的库所实现的一个解析器。lua可以作为c语言的扩展,反过来也可以用c语言编写模块来扩展lua,这两种情况都使用同样的api进行交互。lua与c主要是通过一个虚拟的“栈”来交换数据。

简单的lua解析器

下面是利用lua提供的库来开发的一个简单的解析器。它从标准输入中读入用户的输入,然后输出执行的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio .h>
#include <string .h>
 
#include <lua .h>
#include <lauxlib .h>
#include <lualib .h>
 
int main(void)
{
    int err;
    lua_State* l;
    char buf[BUFSIZ];
 
    l = luaL_newstate();
    luaL_openlibs(l);
 
    while (fgets(buf, BUFSIZ, stdin) != NULL) {
        err = luaL_loadbuffer(l, buf, strlen(buf), "line");
        if (err) {
            fprintf(stderr, "luaL_loadbuffer err: %s", lua_tostring(l, -1));
            lua_pop(l, 1);
            continue;
        }
 
        err = lua_pcall(l, 0, 0, 0);
        if (err) {
            fprintf(stderr, "lua_pcall err: %s", lua_tostring(l, -1));
            lua_pop(l, 1);
        }
    }
 
    lua_close(l);
 
    return 0;
}
</lualib></lauxlib></lua></string></stdio>

阅读全文…

发表于 2012年3月25日

题目起得有点大,其实更多的是软件使用感想,如果理解有错误希望大家指正。

用过emacs后发现插件相当强大(当然不是说vim扩展弱,只是以前基本裸用vim,因为就编辑功能来说,vim本身提供的已经很好了),还有像eclipse和firefox等以强大的插件机制而闻名的软件,于是很好奇这样的机制是怎样实现的呢?在什么情况下才需要对软件进行扩展?扩展机制所提供的自由度有多大?

不管哪个软件似乎都带有一个或一堆配置文件,方便用户对软件的表现形式或行为进行设置以符合个人的习惯。但是带有配置文件只能说明软件可配置,却不一定是可扩展。例如vsftpd和sshd等都有一些配置文件,但是配置项都是一些很简单的key=value的形式,这样的形式只能对程序已有的功能进行调整(例如打开/关闭某个功能,对某些参数如端口,超时间隔等进行配置,图形界面的可能还有换皮肤功能)。在我看来这些程序是可配置的,但却不是可扩展的。软件是否具有可扩展性从配置文件的格式上也能看出一点。例如那些只有一行行(key, value)的配置文件说明这个软件只有简单的开关和调整功能,而支持分支循环结构的配置文件则说明软件的扩展性可能还不错。

什么情况下需要对软件进行扩展?哪些软件需要扩展机制,哪些不需要?

以中文用户每天都要用到的输入法为例。linux上的拼音输入法(不只是拼音输入法,应该说是输入法框架)不多,一只手都能数得过来:scim,ibus,fcitx,好像还有个fitx,不过现在debian的源里已经没有了。总的来说就是,输入法前端提供展现方式(每次显示多少个候选字词,候选字词是横排还是竖排,使用哪款皮肤等),中间是分析引擎(用户输入一串拼音,引擎怎样拆分输入的字符串,比如说用户输入”dier”,是拆成”die’r”还是”di’er”,输入”fangan”是拆成”fang’an”还是”fan’gan”,对于从词库获取的字词是按统计频率排序还是mru排序),引擎下面是不同的词库(对于”die’r”可以从词库里获取一个词还是两个单字,”ri’wan’juan’shu’tou”是出来一整句“日晚倦梳头”还是五个单字还是其它)。这样的输入法结构可以随意替换三个部分里的任何一个,例如在不同平台上使用不同的图形库改写前端,使用更优秀的分析引擎或更齐全的词库。不过我觉得分析引擎和词库应该是一起的,不知道将它们分开对不对。

阅读全文…

发表于 2012年3月10日

年前开始做这个项目,虽然主要的功能放假前已经完成,但是老板为了体现实验室的主题——并行计算,要求把一个简单的hash查找功能弄成复杂的“多线程并行计算”,尽管最后的多线程实现比串行的还慢(通过使用gprof分析发现花在同步上的开销超过了多线程带来的并行效益,因为实际的hash查找不怎么耗时间)。当然这些我是无力改变的,不过有个好处就是老板虽然不懂具体技术,但是却不会瞎指挥,所以在实现上倒是挺自由的,因此可以随便尝试自己想学的东西,只要最后项目完成就行。

好了,发完牢骚之后总结一下从中学到的东西吧。

生产者-消费者问题

我觉得所有的问题基本都可以归结为生产者-消费者问题:输入是生产者,程序处理是消费者。刚开始写的是串行的代码,使用惯用的套路:

int main(void)
{
    ......
 
    while ((rec = producer()))
        consumer(rec);
 
    ......
}

串行程序需要由main函数驱动,主要逻辑由main函数搭建,这样的话如果非要把多线程嵌到这个模型中,看起来会有点奇怪:

阅读全文…

发表于 2012年3月3日

昨天把fcitx 4.2.0编译了一下,装上后发现原来的简繁转换功能没了,到邮件列表上问了下为啥没有了原来4.1.2版的“简”和“繁”转换的图标,被告知该图标换成了“汉”和“漢”(当然事实不是这样,而是原来图标的区域都不见了…),几番折腾后无果。今天从终端启动了一下fcitx,发现出现了下面的错误提示:

[ERROR] /tmp/fcitx-4.2.0/src/lib/fcitx/module.c:65-模块: 打开/usr/lib/fcitx/fcitx-chttrans.so 失败 libopencc.so.1: 无法打开共享对象文件: 没有那个文件或目录

把libopencc1装上就看到转换功能的图标了。

为什么之前没有发现这个原因呢?因为我把fcitx放到gnome的自动启动里,这样出错信息不会直接打在屏幕上,而是重定向到~/.xsession-errors中,而我一般不会去看这个文件。我曾经把这个文件做成到/dev/null的软链接,可是每次gnome都把链接给删了然后重新建一个。看来过于自动化也不是好事。

据解答问题的热心人说,如果编译的时候没有发现opencc的dev库的话是不会使用opencc的,所以我编译的环境一定装了opencc……这是对的,因为软件是在实验室的机器上编译的,而自用的笔记本上并没有装opencc,因此出现了上面这一幕,把opencc删掉后再编译一遍果然好使了。其实debian的unstable源里已经有打包好的程序了,只是因为我用的stable,直接装unstable会有一堆包要删要更新,也就懒得弄了。

相似的事情还发生在配置vim上。前两天想把插入模式下的<C-h>映射为向前跳一个单词,怎样弄都不成功,在网上没找到类似情况,上水木问也没有解决问题。最后发现是和autocomplpop这个插件冲突了,只要把autoload/acp.vim中关于<c-h>的行都注释掉就行了。其实当时发现没效果的时候就应该不加插件启动试一下的,却是因为插件用的时间比较长了,几乎把插件的功能当成vim自带的功能。看来有时习惯了某个东西,很容易就认为是理所当然的,出问题就没往那个方向考虑,而最后发现问题很可能就是这些东西引起的。

有时遇到错误束手无策的时候可以暂时抛开,过后再回头看说不定灵光一闪就解决了,很多看似毫无头绪的错误到最后都被发现是没满足最基本的条件。例如经常会发现学校里教给我们的建立在理想世界基础上的知识和方法,到了现实生活中总是不灵,因为现实生活中的种种限制并不满足方法论成立的条件。而悲哀的是,我们却不会从根本上思考一下,而只是在自己固有的认知上不断挖掘不存在的错误,最后就这样悲剧了。

最后要感谢开发软件的人们,是你们的努力使我的生活更轻松;感谢那些热心为我解答问题的人们,真的很感谢你们,同时为我的无知浪费了你们宝贵的时间向你们道歉。

发表于 2012年2月13日

为了补偿前几周对vim的不忠,这两天整理了一下vim的配置,贴上来备份一下,也算是一篇凑数的文章。(2012.02.15更新)

目前正在使用的插件有autocomplpop(自动弹出补全菜单,但是里面重新map了<c-h>,把autoload/acp.vim中关于<c-h>的行注释掉就行了),bufexplorer(多个buffer间切换),code_complete(代码模板),nerdtree(文件浏览的侧边栏),taglist(代码结构浏览,只需装上ctags即可,不需生成tags文件)。另外还有一些好用的插件如echofunc(输入函数左括号时显示函数原型),omnicppcomplete(c/c++代码补全)等,这些插件需要生成tags索引,因为我主要用cscope,所以就没用这些。还有其它的一些插件,如nerdcommenter(注释的好帮手),neocomplcache(好多功能的集合),fuzzyfinder(也是好多功能的集合)。

最后贴一下配置文件,是在deb包配置文件的基础上修改的:

阅读全文…

发表于 2012年2月9日