不停地疯

Work as a hacker, hack as a artist.

使用tmux改进终端体验

| Comments

之前一直使用GNU Screen作为我的终端管理软件,但是发现它和我使用的Emacs编辑器不兼容,其表现是画面会被无规律的撕裂,经常造成无法正常显示和编辑文件。虽然也尝试过不少配置方法,但是都没有效果。这迫使我去寻找GNU Screen的替代品,直到后来遇到tmux ,才将我从混乱的画面中拯救出来。tmux和Emacs的兼容非常好,没有任何问题,这点让我非常满意。同时,tmux拥有强大的自定义能力,只需简单的配置,就可以使工作环境舒适度显著提高。

首先,先简单了解一下tmux。tmux顾名思义,取terminal multiplexer之意,及终端复用器,其源代码基于BSD协议进行开源和分发。使用上来说,tmux和GNU Screen大同小异,都是使用命令引导键来进行操作,不过tmux的默认引导键由Screen的 C-a 变更为了 C-b 。另外,常用命令也和Gnu Screen一样可以通过 引导键 ? 来查看。操作方法的近似,促使我下决心从GNU Screen转换到tmux下。考虑到tmux作为GNU Screen的改进实现,功能要高级许多,仅仅用来替代GNU Screen有点大材小用的感觉。所以为了更好的学习tmux,我从The Pragmatic Bookshelf购买了名叫 tmux: Productive Mouse-Free Development 的书,并花了3天时间将这本书读完,感到受益匪浅。之后,按照书中的建议配置了工作环境中的tmux,感觉非常好,极大提升了终端工作的效率。下面来看看我的配置:

.tmux.conf配置
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 配置使用和GNU Screen相同的C-a作为命令引导键
set -g prefix C-a
# 设置终端类型为256色
set -g default-terminal "screen-256color"

# 设置状态栏前景及背景色
set -g status-bg colour23
set -g status-fg colour238

# 设置窗口标签的前景及背景色
setw -g window-status-fg colour232
setw -g window-status-bg default
setw -g window-status-attr dim

# 设置当前窗口标签的前景及背景色
setw -g window-status-current-fg colour88
setw -g window-status-current-bg colour130
setw -g window-status-current-attr bright

# 设置窗口分割的边框颜色
set -g pane-border-fg colour189
set -g pane-border-bg black

# 设置当前窗口分割的边框颜色
set -g pane-active-border-fg white
set -g pane-active-border-bg colour208

# 设置提示信息的前景及背景色
set -g message-fg colour232
set -g message-bg colour23
set -g message-attr bright

# 设置状态栏左部宽度
set -g status-left-length 40
# 设置状态栏显示内容和内容颜色。这里配置从左边开始显示,使用绿色显示session名称,黄色显示窗口号,蓝色显示窗口分割号
set -g status-left "#[fg=colour52]#S #[fg=yellow]#I #[fg=cyan]#P"
# 设置状态栏右部宽度
set -g status-right-length 80
# 设置状态栏右边内容,这里设置为时间信息
set -g status-right "#[fg=colour106]#(~/bin/system_info.sh) #[fg=colour208]|%d %b %R"
# 窗口信息居中显示
set -g status-justify centre

# 监视窗口信息,如有内容变动,进行提示
setw -g monitor-activity on
set -g visual-activity on
set -g status-utf8 on

# 窗口号和窗口分割号都以1开始(默认从0开始)
set -g base-index 1
setw -g pane-base-index 1

# 支持鼠标选择窗口,调节窗口大小
setw -g mode-mouse on
set -g mouse-select-pane on
set -g mouse-resize-pane on
set -g mouse-select-window on
set -s escape-time 1

# 设置C-a a为发送C-a键
bind a send-prefix
# 加载tmux配置文件的快捷键
bind r source-file ~/.tmux.conf\; display "Reloaded!"
# 快捷键查看man
bind / command-prompt "split-window 'exec man %%'"
unbind "%"
unbind "\""
# 修改默认的窗口分割快捷键,使用更直观的符号
bind | split-window -h
bind - split-window -v
# 选择窗口功能修改为和Screen一样的C-a "
bind "\"" choose-window

# 选择窗口分割快捷键
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# 选择窗口快捷键
bind -r C-h select-window -t :-
bind -r C-l select-window -t :+
# 调节窗口大小快捷键
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# 快捷调整窗口分割到全屏
unbind Up
bind Up new-window -d -n tmp \; swap-pane -s tmp.1 \; select-window -t tmp
unbind Down
bind Down last-window \; swap-pane -s tmp.1 \; kill-window -t tmp

# 快捷记录窗口内的内容到文件中
bind P pipe-pane -o "cat >>~/#W.log" \; display "Toggled logging to ~/#W.log"

以上配置只需要复制保存到 ~/.tmux.conf 文件中,下次执行tmux时就生效了。

当然,tmux的高级不止在于配置功能的强大,它还支持在命令行中对指定session进行设置。利用这个特性,便可以将繁琐的工作环境初始化用脚本完成了。比如我写了如下脚本对我的工作电脑进行初始化:

init_tmux.sh
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#! /bin/bash
export AP_7x27_PROJECT="~/Developer/MSM7x27A-ICS-AP"
export MP_7x27_PROJECT="~/Developer/MSM7x27A-ICS-MP"

export AP_8x25_PROJECT="~/Developer/MSM8x25-ICS-AP"
export MP_8x25_PROJECT="~/Developer/MSM8x25-ICS-MP"

if [ -z "$TMUX" ]; then
    tmux has-session -t development7x27
    if [ $? != 0 ]; then
        # init 7x27 AP
        tmux new-session -s development7x27 -n AP_7x27 -d
        tmux send-keys -t development7x27 "cd $AP_7x27_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 13 1" C-m
        tmux split-window -h -p 40 -t development7x27:1
        tmux send-keys -t development7x27 "cd $AP_7x27_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 13 1" C-m
        tmux split-window -v -t development7x27:1.2
        tmux send-keys -t development7x27 "cd $AP_7x27_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 13 1" C-m

        # init 7x27 MP
        tmux new-window -n MP_7x27 -t development7x27

        tmux send-keys -t development7x27:2 "cd $MP_7x27_PROJECT/modem_proc/build/ms" C-m
        tmux split-window -h -p 40 -t development7x27:2
        tmux send-keys -t development7x27:2 "cd $MP_7x27_PROJECT/modem_proc/build/ms" C-m
        tmux split-window -v -t development7x27:2.2
        tmux send-keys -t development7x27 "cd $MP_7x27_PROJECT/modem_proc/build/ms" C-m

        tmux select-window -t development7x27:1
        tmux select-pane -t development7x27:1 -L
    fi

        tmux send-keys -t development7x27:1.3 "export DISPLAY=$DISPLAY" C-m
        tmux send-keys -t development7x27:2.3 "export DISPLAY=$DISPLAY" C-m

    tmux has-session -t development8x25
    if [ $? != 0 ]; then
        # init 8x25 AP
        tmux new-session -s development8x25 -n AP_8x25 -d
        tmux send-keys -t development8x25 "cd $AP_8x25_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 17 3" C-m
        tmux split-window -h -p 40 -t development8x25:1
        tmux send-keys -t development8x25 "cd $AP_8x25_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 17 3" C-m
        tmux send-keys -t development8x25 'top' C-m
        tmux split-window -v -t development8x25:1.2
        tmux send-keys -t development8x25 "cd $AP_8x25_PROJECT&&. ./build/envsetup.sh&&choosecombo 1 17 3" C-m

        # init 8x25 MP
        tmux new-window -n MP_8x25 -t development8x25

        tmux send-keys -t development8x25:2 "cd $MP_8x25_PROJECT/modem_proc/build/ms" C-m
        tmux split-window -h -p 40 -t development8x25:2
        tmux send-keys -t development8x25:2 "cd $MP_8x25_PROJECT/modem_proc/build/ms" C-m
        tmux split-window -v -t development8x25:2.2
        tmux send-keys -t development8x25 "cd $MP_8x25_PROJECT/modem_proc/build/ms" C-m

        tmux select-window -t development8x25:1
        tmux select-pane -t development8x25:1 -L
    fi

        tmux send-keys -t development8x25:1.3 "export DISPLAY=$DISPLAY" C-m
        tmux send-keys -t development8x25:2.3 "export DISPLAY=$DISPLAY" C-m

    tmux attach -t development7x27
fi
脚本主体思想为每次运行时判断相应的tmux session是否存在,如果存在则设置Xwindow的变量后attach;如果不存在相应session,则新建相应session并初始化session中相应窗口和窗口分割,同时在每个窗口分割中运行每次都要运行的环境初始化命令。最后设置Xwindow环境变量后attach。我的脚本中分别初始化了高通7x27 AP和MP的编译环境以及8x25 AP和MP的编译环境。

使用时,将以上内容存为文件,并在 ~/.bashrc 中调用就可以了。这样,不论是ssh到该主机还是新开一个终端窗口,都会直接进入指定的tmux session中,继续之前的工作。加上Xwindow的设置,tmux中也可以直接运行X程序。工作中,我就是在windows上使用putty+Xming来运行使用X程序的,非常方便高效。简单的配置让工作环境大幅改进,让我觉得之前6刀买到那本书真是超值了。

说了这么多好,tmux其实也是有缺点的。最明显的一个缺点就是不支持windows,而GNU Screen却支持是windows的,这不免让人有点遗憾。所以如果有在Windows下使用类似软件的话(真的有需要吗?),只能考虑其它如GNU Screen之类的软件了。

最近看完的两本书

| Comments

近日看完两本关于Slackware的入门书,一本是大名鼎鼎的《Slackware Linux Essentials》,另一本是《Slackware Linux Basics》。之所以想起来看Slackware相关的书,是因为人生接触的第一个Linux发行版就是Slackware。那时候家里没有网络,从报纸上知道了开源软件和Linux的存在。后来在书店里看到了电子工业出版社出版的《Linux大全》,那是一本很厚的书,书的背面印着获取书中描述的Linux版本的方法,我就是从那里获取到的人生第一个Linux发行版。原版书提供三个Linux发行版可供选择,而电子工业出版社只提供其中的两个发行版,一个是人们熟知的RedHat,而另一个就是Slackware。当时本人处于对计算机技术极度痴迷的状态,总是希望能有一些挑战,所以选择邮购了其中安装及配置较为复杂的一个发行版,也就是Slackware。当然,最后从拿到Slackware光盘,到安装并简单配置到能使用确实花了不少时间,也因为如此,我对Slackware的印象才如此深刻。之后在大学的时候自己买过RedHat/Fedora并用了那么几次,工作了后接触更多的是Ubuntu,但是随着Linux使用经验的逐渐增长以及知识的不断积累,越发觉得之后使用的那些Linux发行版似乎都缺少了一点什么,这种感觉说不上来,但是这种隐隐约约的感觉将我的目光再次引向Slackware发行版。

碰巧,前段时间从网上买过一台Raspberry Pi1,而且正好,又有爱好者将ArmedSlack2移植到了Raspberry Pi上,理所当然,我的Raspberry Pi便跑上了Slackware。可惜,虽说最早接触的是Slackware,但是自己对Slackware的了解并不系统,所以下定决心说一定要看完至少一本关于Slackware的著作。于是,才从网上找到上述的两本书。还好,从目前情况来看,这个任务算是完成了,而且超标了。

现在说说才看完的这两本书,作为Slackware Linux的官方教材《Slackware Linux Essentials》3更新并不频繁,目前作者正在编写第三版,但是该版尚处于Beta状态4,所以市面上能买到的只有第二版。鉴于前者更新不够及时,导致了《Slackware Linux Basics》5的出现。从名字上看,前者似乎比后者更加全面,但从本人阅读下来的感觉来说,Basics的内容明显要比Essentials的深入一些;从页数上来说,Basics的相比也要多一些,但是这并不是说两本书哪本更好。单从入门角度来讲,我觉得Essentials的内容更适合入门学习;Basics的内容作为Essentials的补充,适合进阶学习。总体来讲,两本书可以互相参考学习。就本人经验,看完其中一本后,另外一本可以很快看完,因为只需要看差异的部分就可以了。虽然这两本书都以Slackware为切入点,但是其内容对于其它发行版也是部分适用的。所以,不论对于想学Slackware还是想学Linux的同学,我是极力推荐去看这两本书的,比市面上买的那些翻译山寨教学书好过不少。至于需要书的同学,可以去脚注找找,两本书官方都提供常用电子档下载,而我是用软件将HTML转成了kindle格式在kindle上读完这两本书的。

另外,做个预告,我打算将Raspberry Pi打造为基于ArmedSlack系统的“科学上网机”,目前基本成型,具体实现方法和相关软件会随着博客的更新逐步公开。

小脚本帮大忙

| Comments

前两天单位的在做项目中发现一个Bug:手机在启动后触摸屏有一定几率无法使用。这个Bug非常恼人,因为重现几率非常低,而且只会出现在重启过程中。这意味着不论是调查原因还是验证对策,都将是非常耗时且繁琐的。因为对策问题之前,首先需要重现问题,如此才好分析问题的原因。而这个Bug必须要反复重启手机才能重现,人为操作的话太浪费时间效率低下。 好在这个Bug的行为比较稳定,重现后触屏肯定不能使用。通过adb对比调查正常手机和问题手机的设备节点,发现在出问题的手机中,触屏设备没有注册成功。看来是设备注册失败,导致的触屏异常。于是,我们考虑使用脚本对该Bug进行再现分析。思路如下:

  1. 写一个脚本判断触屏驱动的设备节点是否注册成功,如果成功则复位重启;否则保留现场等待分析。
  2. 将该脚本添加到 init.rc 中成为一个服务,在启动时调用。
  3. 脚本运行时将相应的运行信息输出到外部文件中,从而可以计算出再现率。

基于以上想法,写出了以下脚本代码:

check_tp.sh
1
2
3
4
5
6
7
8
9
#!/system/bin/sh
tp_name="xxxx" # xxxx为注册的tp名称
input_name=`cat /sys/class/input/input0/name`
if [ "x_$tp_name" = "x_$input_name" ]; then
    echo "`date` touch screen is OK." >> /data/check_tp.log
    reboot
else
    echo "`date` touch screen is not OK." >> /data/check_tp.log
fi
同时,修改 init.rc 文件,在其中加入:
1
2
3
service check_tp /system/bin/sh /system/bin/check_tp.sh
    class main
    oneshot
然后重新编译bootimage并刷机。最后使用 adb remount&&adb push check_tp.sh /system/bin/&&adb shell chmod 755 /system/bin/check_tp.sh ,将刚才新写的脚本推送到手机上。重启手机,之后就会看到手机不断的上电然后复位重启。 在运行该脚本不断重启手机8小时之后,手机正常进入了系统。此时操作手机进行验证,发现触屏已经无效。分析 /data/check_tp.log 文件,算出手机共重启了1000多次,从而得出该问题的再现率大概为千分之一。利用该脚本,验证bug方便了好多,大大提高了工作效率。

后记

由于以上写的脚本使用到了 if 关键字,而Android系统默认不支持该关键字,必须依赖busybox环境才行。之前我有移植过busybox,但是只在工程模式下生效,所以该脚本在release版本中是不能正常运行的。为了不依赖运行环境,我又将该脚本换了一种写法,改为:

check_tp2.sh
1
2
3
4
5
6
7
8
9
10
#!/system/bin/sh
tp_name="xxxx" # xxxx为注册的tp名称
input_name=`cat /sys/class/input/input0/name`
case $input_name in
    $tp_name) echo "`date` touch screen is OK." >> /data/check_tp.log
              reboot
             ;;
    *) echo "`date` touch screen is not OK." >> /data/check_tp.log
            ;;
esac
这样,即便是在Android原生环境中,也可以正确无误的运行。这样就能将该脚本发给测试,利用它对release版本进行bug验证了。

推荐使用Vitamin-R

| Comments

记得之前Twitter上的@justin7974介绍过Vitamin-R这个软件,是一款个人时间管理类的软件,据说可以和Omnifocus 或者Things配合起来用,看起来不错的样子。只是它20刀的价钱相对它的功能来说,我觉得还是贵了点,所以一直以来都没有下手。前段时间浏览Twitter,突然看见一条新闻,说Vitamin-R在AppStore 上1折优惠只要2刀。于是,毫不犹豫的下单买了它。

Vitamin-R运行起来的界面非常简单,运行时会在菜单栏上出来一个”R”图标。点击这个图标就会出现如下界面:

./images/blog/./79722mqA.png

截图中是我自己设置的一个”写博客“的工作目标,时长设置为20分钟。设置了目标后,自己就可以和时间赛跑开始干活了。软件默认会在一定时间过后用声音提示用户,以注意所消耗的时间。等倒计时结束,他会以用户设定的方式提醒用户设定的时间已到,并要求用户对自己这段时间的工作情况进行评价和记录。如此,用户便可以对自己完成一个任务所消耗的时间以及投入程度有一个了解,以便未来更高效的工作和制定计划。

说到Vitamin-R最让我关注的一个功能,那就是和Omnifocus的整合了。使用也非常简单,直接将Omnifocus中的任务条目用鼠标拖进Vitamin-R的时间片描述中,然后设置任务时间开始即可。任务时间到期后会出现如下这个界面:

./images/blog/./79722NJT.png

将下面两个复选框选中,点继续,然后完成。如此这样,你会发现Omnifocus中的任务也会相应的被标记为完成状态。

此外,Vitamin支持丰富的快捷键,比如使用CMD-OPT-N,可以打开一个类似便签的界面,用来记录当前任务的一些随笔想法:

./images/blog/./79722ndf.png

如截图中所示,除了记录当前任务,还能记录一些其它内容。而这些功能都可以通过快捷键来很方便的调用。

当然,Vitamin-R还有一些很有用的功能,比如噪声发生器(Noise Machine)。可以让用户在安静的工作环境中设置一些噪音,模拟自然界的声音,以让用户更关注工作的内容。

好了,就介绍到这里。还是那句话,软件本身使用很简单,设置项也不多,至于值不值20刀的价格就见仁见智了,不过2刀是绝对值了:)

给Octopress的RSS输出中添加文章作者信息

| Comments

Octopress自带的RSS模版中没有包含文章作者信息,而是只包含了网站作者信息。考虑到博客经常只是一个人写作,同时文章作者随RSS显示,更常用一些,所以我对默认的RSS模版做了一点改动,让它支持了文章作者信息的输出。其实改动相当简单,默认情况下就一个 atom.xml 文件,该文件位于 source/ 目录下。改动如下:

atom.xml输出作者信息 (atom_author.diff) download
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
diff --git a/source/atom.xml b/source/atom.xml
index 83af3f8..c20506b 100644
--- a/source/atom.xml
+++ b/source/atom.xml
@@ -9,10 +9,6 @@ layout: nil
   <link href="{{ site.url }}/"/>
   <updated>{{ site.time | date_to_xmlschema }}</updated>
   <id>{{ site.url }}/</id>
-  <author>
-    <name><![CDATA[{{ site.author | strip_html }}]]></name>
-    {% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
-  </author>
   <generator uri="http://octopress.org/">Octopress</generator>

   {% for post in site.posts limit: 20 %}
@@ -20,8 +16,17 @@ layout: nil
     <title type="html"><![CDATA[{{ post.title | cdata_escape }}]]></title>
     <link href="{{ site.url }}{{ post.url }}"/>
     <updated>{{ post.date | date_to_xmlschema }}</updated>
+    <author>
+      <name><![CDATA[{{ site.author | strip_html }}]]></name>
+      {% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
+    </author>
     <id>{{ site.url }}{{ post.id }}</id>
     <content type="html"><![CDATA[{{ post.content | expand_urls: site.url | cdata_escape }}]]></content>
   </entry>
   {% endfor %}
 </feed>
其实很简单,就是将全局的作者设定移到文章条目的设定中,这样设置的作者信息就成为了文章的属性。 对于曾经给Octopress添加过RSS2.0支持的站点1,还需要相应修改一下 source/ 目录下 rss.xml 文件:
rss.xml输出作者信息 (rss_author.diff) download
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
diff --git a/source/rss.xml b/source/rss.xml
index 435f17f..044e52f 100644
--- a/source/rss.xml
+++ b/source/rss.xml
@@ -3,27 +3,31 @@ layout: nil
 ---
 <?xml version="1.0" encoding="UTF-8"?>
 <rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <description><![CDATA[{{ site.title }}]]></description>
    <title><![CDATA[{{ site.title }}]]></title>
    <link>{{ site.url }}/</link>
    <pubDate>{{ site.time | date_to_xmlschema }}</pubDate>

    {% for post in site.posts limit: 20 %}
    <item>
      <description>
	<![CDATA[
		 {{ post.content | expand_urls: site.url | cdata_escape }}
	]]>
      </description>
      <title><![CDATA[{{ post.title | cdata_escape }}]]></title>
      <link>{{ site.url }}{{ post.url }}</link>
      <pubDate>{{ post.date | date_to_xmlschema }}</pubDate>
      <guid isPermaLink="false">{{ site.url }}{{ post.id }}</guid>
      <source url="{{ site.url }}/rss.xml"><![CDATA[{{ site.title }}]]></source>
+      <author>
+	<name><![CDATA[{{ site.author | strip_html }}]]></name>
+	{% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
+      </author>
    </item>
    {% endfor %}
  </channel>
 </rss>
同样,只需在文章描述段中加入作者信息的属性即可。修改好文件保存之后,再次运行 rake generate 就会发现rss文件已经正确更新了。

另外,有人还想在RSS输出的文章中加入相应版权声明,但又不想将版权声明嵌到文章里(比如像我这样)。办法也是有的,其实还是修改RSS模版文件。 首先在 source/_includes/post/ 目录中添加一个叫 copyright.html 新文件,内容如下,也可以是自己自定的一些版权内容:

版权声明文件 (copyright.diff) download
1
2
3
4
5
6
7
8
9
10
11
12
13
diff --git a/source/_includes/post/copyright.html b/source/_includes/post/copyright.html
index 2e57b7e..9547c7b 100644
--- a/source/_includes/post/copyright.html
+++ b/source/_includes/post/copyright.html
@@ -1,2 +1,8 @@
+<p class='post-footer'>
+  <h1>License</h1>
+  <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh"><img alt="知识共享许可协议" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /></a><br />本博作品采用<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh">知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议</a>进行许可。<br/>
+  Original link:
+  <a href='{{ site.url }}{{ post.url }}'>{{ site.url }}{{ post.url }}</a><br/>
+  &nbsp;written by <a href='{{ site.url }}'>{{ site.author }}</a>
+  &nbsp;posted at <a href='{{ site.url }}'>{{ site.url }}</a>
+</p>
我的版权声明模版中加入了CC许可,文章原始链接,作者名称以及个人站点信息。然后分别修改 atom.xmlrss.xml 文件如下:
atom.xml添加版权声明 (atom_copyright.diff) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
diff --git a/source/atom.xml b/source/atom.xml
index 83af3f8..c20506b 100644
--- a/source/atom.xml
+++ b/source/atom.xml
@@ -20,8 +16,17 @@ layout: nil
     <title type="html"><![CDATA[{{ post.title | cdata_escape }}]]></title>
     <link href="{{ site.url }}{{ post.url }}"/>
     <updated>{{ post.date | date_to_xmlschema }}</updated>
    <author>
      <name><![CDATA[{{ site.author | strip_html }}]]></name>
      {% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
    </author>
     <id>{{ site.url }}{{ post.id }}</id>
-    <content type="html"><![CDATA[{{ post.content | expand_urls: site.url | cdata_escape }}]]></content>
+    <content type="html">
+        <![CDATA[
+        {{ post.content | expand_urls: site.url | cdata_escape }}
+		{% include post/copyright.html %}
+        ]]>
+    </content>
   </entry>
   {% endfor %}
 </feed>
就添加一句 include post/copyright.html ,不过要注意是在CDATA段中。 rss.xml 一样处理:
rss.xml添加版权声明 (rss_copyright.diff) download
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
diff --git a/source/rss.xml b/source/rss.xml
index 435f17f..044e52f 100644
--- a/source/rss.xml
+++ b/source/rss.xml
@@ -3,27 +3,31 @@ layout: nil
 ---
 <?xml version="1.0" encoding="UTF-8"?>
 <rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <description><![CDATA[{{ site.title }}]]></description>
    <title><![CDATA[{{ site.title }}]]></title>
    <link>{{ site.url }}/</link>
    <pubDate>{{ site.time | date_to_xmlschema }}</pubDate>

    {% for post in site.posts limit: 20 %}
    <item>
      <description>
	<![CDATA[
		 {{ post.content | expand_urls: site.url | cdata_escape }}
+		 {% include post/copyright.html %}
	]]>
      </description>
      <title><![CDATA[{{ post.title | cdata_escape }}]]></title>
      <link>{{ site.url }}{{ post.url }}</link>
      <pubDate>{{ post.date | date_to_xmlschema }}</pubDate>
      <guid isPermaLink="false">{{ site.url }}{{ post.id }}</guid>

      <source url="{{ site.url }}/rss.xml"><![CDATA[{{ site.title }}]]></source>
      <author>
	<name><![CDATA[{{ site.author | strip_html }}]]></name>
	{% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
      </author>
    </item>
    {% endfor %}
  </channel>
 </rss>
轻松搞定,效果就是我博客现在的效果。

在Android编译时建立符号链接

| Comments

最近接到一个任务,需要将Busybox环境移植到高通平台的Android项目上。Busybox的目标执行文件有现成编译好的,需要做的工作就是添加一个Android工程,将编译好的二进制文件拷贝到Android的文件系统中,同时还需要生成相应的Busybox命令链接。

拷贝文件到指定目录在Android的编译系统中有现成的方法,使用下面这个方法即可:

拷贝文件到指定目录
1
2
3
4
5
6
7
8
9
10
11
LOCAL_PATH := $(call my-dir)

# Add busybox environment zhiqiang.xu 2012.10.8
include $(CLEAR_VARS)
LOCAL_MODULE := busybox_modules
LOCAL_MODULE_STEM := busybox
LOCAL_SRC_FILES := busybox
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_OUT)/busybox
include $(BUILD_PREBUILT)
由于Busybox环境主要用于工程调试,所以模块设置为只在eng编译模式下有效。按照以上这段设置,可以将busybox执行文件拷贝到Android文件系统中的 /system/busybox 目录下。

而对于生成链接,之前没有接触过。不过在搜索调查了已有的Android工程文件之后,发现系统中也提供了现成的方法,如下:

recovery使用的软链生成方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links)
ifndef BOARD_HAS_SMALL_RECOVERY
exclude := tune2fs
ifeq ($(BOARD_HAS_LARGE_FILESYSTEM),true)
exclude += mke2fs
endif
endif
RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox
$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
        @echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
        @mkdir -p $(dir $@)
        @rm -rf $@
        $(hide) ln -sf $(BUSYBOX_BINARY) $@

ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS)
实现方法使用了标准的Makefile目标,在 $(RECOVERY_BUSYBOX_SYMLINKS) 目标下生成相应链接,看起来似乎是只要将Makefile的目标添加到 ALL_DEFAULT_INSTALLED_MODULES 这个变量后,编译的时候就会按照Makefile的标准生成目标。实验后确实可行,不过同时我也发现了一个不足。那就是使用这种方法后,通过mm/mmm命令进行模块编译的时候是没法正确执行的。换言之, ALL_DEFAULT_INSTALLED_MODULES 变量只有在系统完全编译的时候才会被调用。为此,领悟范例的精神后,自己改了一下实现方法,如下:

修改后的生成软链的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))

define _make_link
   $(shell echo "Symlink: $(1) -> $(2)")
   $(shell mkdir -p $(dir $(1)))
   $(shell rm -rf $(1))
   $(shell ln -sf $(2) $(1))
endef

# Add busybox environment zhiqiang.xu 2012.10.8
# Now let's do busybox symlinks
BUSYBOX_LINKS := $(shell cat $(LOCAL_PATH)/busybox.links)
BUSYBOX_SYMLINKS := $(addprefix $(TARGET_OUT)/busybox/,$(notdir $(BUSYBOX_LINKS)))
BUSYBOX_BINARY := /system/busybox/$(LOCAL_SRC_FILES)
$(foreach _item, $(BUSYBOX_SYMLINKS), \
       $(eval $(call _make_link,$(_item),$(BUSYBOX_BINARY))))

_make_link :=
endif

修改后的方法使用了自定义宏,不论是全系统编译还是使用mm/mmm进行模块编译,每次编译的时候宏都会展开执行。同时为了区分编译模式,我又添加了相应的判断宏 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) 将执行部分包括在里面。实验下来效果良好,可以根据当前设定的编译模式生成或者不生成相应软链。 最后,发现一点,我修改的这个方法由于没有依赖目标,所以每次编译的时候都会执行一遍,编译效率不高,所以这种结构不能用于大负荷的处理工作。好在生成软链不是多重的工作,这么用用也无什么大碍。

为Tomato添加用户

| Comments

手上的RT-N16跑Tomato系统已经两年有余了,两年来Tomato系统没有让我失望过,一直提供着稳定高速的网络服务,此外它还提供了内网文件服务,P2P下载以及科学上网代理等重要功能。最近,本人又败了一个小玩意:Raspberry Pi , 税前价格$25,到手¥310。入手后,我在上面安装了ArmedSlack,运行的非常稳定,很不错。于是,本人决定将其打造为一个随身的功能强大的小电脑,包括随插随用的科学上网代理。基本思路是Raspberry Pi启动后通过公钥认证自动连接到家中的RT-N16路由器上,然后通过ssh转发相应端口,即可实现科学上网。不过,这个方法中有一个隐患:因为Tomato默认只提供root/admin账户,权限太大,如果Raspberry Pi使用这个账户进行连接,总是有点不放心。于是,我决定给Tomato系统添加新的用户。

Tomato默认并不支持添加账户,即便通过optware安装了adduser后也由于没有passwd命令而失败。不过通过网络搜索,还是让我找到了一个可以添加用户的方法1。我用如下的命令在Tomato中建立了一个名为sshuser的用户:

添加用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
UNAM=sshuser
RNAM="For Login Only"
UNUM=200
UGRP=$UNUM
#UGRP=0
echo "$UNAM:x:$UNUM:$UGRP:$RNAM:/tmp:/bin/sh" >> /etc/passwd
echo "$UNAM:x:$UNUM:$UGRP:$RNAM:/home/$UNAM:/bin/sh" >> /etc/passwd.custom
[[ $UGRP -ne 0 ]] && echo "$UNAM:x:$UGRP:" >>/etc/group
[[ $UGRP -ne 0 ]] && echo "$UNAM:x:$UGRP:" >>/etc/group.custom
sed -n -e "s,^root:,$UNAM:,p" < /etc/shadow >> /etc/shadow.custom

chmod 777 /tmp/home
ssh $UNAM@localhost "mkdir /home/$UNAM;touch /home/$UNAM/.profile && echo success"
# press return for the password prompt, you should see the word "success" reported

chmod 755 /tmp/home

nvram setfile2nvram /etc/passwd.custom
nvram setfile2nvram /etc/group.custom
nvram setfile2nvram /etc/shadow.custom
nvram setfile2nvram /home/$UNAM/.profile
nvram commit
最后几句包含nvram的语句是将新建的几个 .custom 文件添加到nvram中,这样这些新添加的文件就可以在重启路由器后还能存在。然后,将以下三句添加到路由器设置中->脚本设置->初始化中:
初始化用户
1
2
3
sed -i "/^sshuser:/d" /etc/passwd
grep "^sshuser:" < /etc/shadow.custom >> /etc/shadow
grep "^sshuser:" < /etc/passwd.custom >> /etc/passwd
如图: ./images/blog/./574rHz.png 新建的用户使用和root一样的密码,如果需要修改,需要相应修改 /etc/shadow 文件。新建用户登录后的效果入下图: ./images/blog/./574dRC.png

调节Android拨号键盘的震动效果

| Comments

最近在做高通Android项目时遇到一个问题,测试报告说拨号键盘中按键震感偏弱,问题首先提到了我这里。于是,我首先去内核驱动里检查了一下振子的配置,发现给振子的供电已经调到最高值了,看来只能从别的方向下手解决这个问题。震感强烈与否取决于两个因素,一个是驱动电流/驱动电压,而另一个是驱动时长。由于这个项目中给振子的供电是个LDO,只能调节驱动电压,而同时驱动电压已经最大,所以需要想办法加长驱动时间。

在和应用一块分析了Android拨号键盘应用的代码后,按键震动的实现在 packages/apps/Contacts/src/com/android/contacts/dialpad/DialpadFragment.java 文件中。该文件中实现了拨号键盘按钮 onClick 事件的监听,其中:

DialpadFragment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.one: {
            playTone(ToneGenerator.TONE_DTMF_1);
            keyPressed(KeyEvent.KEYCODE_1);
            return;
        }
        case R.id.two: {
            playTone(ToneGenerator.TONE_DTMF_2);
            keyPressed(KeyEvent.KEYCODE_2);
            return;
        }
        case R.id.three: {
            playTone(ToneGenerator.TONE_DTMF_3);
            keyPressed(KeyEvent.KEYCODE_3);
            return;
        }
//以下省略
}
可以看到该监听事件中播放了按键音,同时调用了 keyPressed 这个方法。再来看 keyPressed 方法的实现:
DialpadFragment.java
1
2
3
4
5
6
7
8
9
10
11
private void keyPressed(int keyCode) {
    mHaptic.vibrate();
    KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    mDigits.onKeyDown(keyCode, event);

    // If the cursor is at the end of the text we hide it.
    final int length = mDigits.length();
    if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
        mDigits.setCursorVisible(false);
    }
}
注意 mHaptic.vibrate() ,从方法名称上可以看出这个方法和震动相关,所以现在只要搞清楚该方法的具体实现即可。继续往下追,从该类变量的声明 private HapticFeedback mHaptic = new HapticFeedback(); 可以看到,这个方法属于一个名叫 HapticFeedback 的类。而 HapticFeedback 类存在于 packages/apps/Phone/src/com/android/phone/HapticFeedback.java 文件中。顺利找到 vibrate() 的定义:
HapticFeedback.java
1
2
3
4
5
6
public void vibrate() {
    if (!mEnabled || !mSettingEnabled) {
        return;
    }
    mVibrator.vibrate(mHapticPattern, NO_REPEAT);
}
其中的判断无需关心,通过名称可以看出应该是和震动设定有关。之后调用了另一个类 Vibratorvibrate 方法。该方法接受两个参数,同样通过名称看得出第一个参数有关模式,第二个参数有关是否重复。去 Vibrator 类里看看,该类存在于 frameworks/base/core/java/android/os/Vibrator.java 文件中,找到 vibrate 的实现1
Vibrator.java
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
/**
 * Vibrate with a given pattern.
 *
 * <p>
 * Pass in an array of ints that are the durations for which to turn on or off
 * the vibrator in milliseconds.  The first value indicates the number of milliseconds
 * to wait before turning the vibrator on.  The next value indicates the number of milliseconds
 * for which to keep the vibrator on before turning it off.  Subsequent values alternate
 * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
 * </p><p>
 * To cause the pattern to repeat, pass the index into the pattern array at which
 * to start the repeat, or -1 to disable repeating.
 * </p>
 *
 * @param pattern an array of longs of times for which to turn the vibrator on or off.
 * @param repeat the index into pattern at which to repeat, or -1 if
 *        you don't want to repeat.
 */
public void vibrate(long[] pattern, int repeat)
{
    if (mService == null) {
        Log.w(TAG, "Failed to vibrate; no vibrator service.");
        return;
    }
    // catch this here because the server will do nothing.  pattern may
    // not be null, let that be checked, because the server will drop it
    // anyway
    if (repeat < pattern.length) {
        try {
            mService.vibratePattern(pattern, repeat, mToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to vibrate.", e);
        }
    } else {
        throw new ArrayIndexOutOfBoundsException();
    }
}
从注释可以了解到该震动模式的意义,第一个值为等待开启震动的时间,第二个为开启震动后持续的时间,之后交替数字为关闭震动的时间以及开启震动的时间。有兴趣可以追到 frameworks/base/services/java/com/android/server/VibratorService.java 看看 vibratePattern 的实现。不过我们已经找到需要的一切了。OK,再次回到 packages/apps/Phone/src/com/android/phone/HapticFeedback.java 文件中查看传入的震动模式设置,很简单搜到以下处理:
HapticFeedback.java
1
2
3
4
5
6
7
8
9
10
11
public void init(Context context, boolean enabled) {
     mEnabled = enabled;
     if (enabled) {
         mVibrator = new Vibrator();
         if (!loadHapticSystemPattern(context.getResources())) {
             mHapticPattern = new long[] {0, DURATION, 2 * DURATION, 3 * DURATION};
         }
         mSystemSettings = new Settings.System();
         mContentResolver = context.getContentResolver();
     }
}
首先尝试从系统设置里载入震动模式,否则使用默认的 new long[] {0, DURATION, 2 * DURATION, 3 * DURATION}; 模式。其中 DURATION 等于10,所以默认的模式为等待0秒,震10毫秒,停20毫秒,之后震动30毫秒。注释掉 if (!loadHapticSystemPattern(context.getResources())) 判断,然后将默认震动模式改为 {0, 6*DURATION, 1 * DURATION, 6 * DURATION}; 试试效果。执行:
编译
1
mmm packages/apps/Contacts/&&mmm packages/apps/Phone/
然后将编译好的apk文件推到手机上,震感明显,说明修改正确2。现在只需要去xml文件中找到震动模式的设置部分,相应修改就可以了。最后找到 frameworks/base/core/res/res/values/config.xml 文件,其中有一部分为:
设置
1
2
3
4
5
6
7
<!-- Vibrator pattern for feedback about touching a virtual key -->
<integer-array name="config_virtualKeyVibePattern">
    <item>0</item>
    <item>10</item>
    <item>20</item>
    <item>30</item>
</integer-array>
是不是和之前看到的默认震动模式很像呢?最后,根据具体情况调了一个比较适中的值,任务完成。

Footnotes:

1 还有另外一个 vibrate 的实现,但是该实现只接受一个参数,所以不是我们要找的目标。

2 修改震动设置后,必须同时编译Contact和Phone,并同时更新到手机上才能生效,具体原因不明白,感觉很怪异。

关于在Org-Mode下方便插入截图的方法

| Comments

本来使用 Org-Mode 来写博客就很舒服了, 插入图片也很容易,使用

[[图片地址]]

的方法就可以。但是对于编写需要插入截图的文档来说,就得先截图,然后保存图片到相应位置,之后才能使用上面的方法来插入图片。

有幸,前面搜索资料时发现了一篇文章1,其中介绍了一种更加自动化的方式在 Org-Mode 中插入截图。试用后觉得非常方便,不过我又做了一些改动,以适应Mac OSX。同时为使用Octopress,重新设置了图片目录,如下:

my-screenshot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defun my-screenshot ()
  "Take a screenshot into a unique-named file in the current buffer file
directory and insert a link to this file."
  (interactive)
  (setq filename
        (concat (make-temp-name "./") ".png"))
  (setq fullfilename
                 (concat (file-name-directory (buffer-file-name)) "images/blog/" filename))
  (if (file-accessible-directory-p (concat (file-name-directory
                                            (buffer-file-name)) "images/blog/"))
      nil
    (make-directory "images/blog/" t))
  (call-process-shell-command "screencapture" nil nil nil nil "-i" (concat
                                                            "\"" fullfilename "\"" ))
  (insert (concat "[[./images/blog/" filename "]]"))
  (org-display-inline-images)
)
另外,还需要给 org-octopress.el 打个补丁,否则发布文档中图片的索引会有问题,造成某些页面下无法显示:
org-octopress.el补丁
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
diff --git a/org-octopress.el b/org-octopress.el
index 7f87742..36eed86 100644
--- a/org-octopress.el
+++ b/org-octopress.el
@@ -961,7 +961,7 @@ OPT-PLIST is the export options list."
          (if (string-match "^file:" desc)
              (setq desc (substring desc (match-end 0)))))
        (setq desc (org-add-props
-                      (concat "<img src=\"" desc "\" alt=\""
+                      (concat "<img src=\"/" desc "\" alt=\"/"
                               (file-name-nondirectory desc) "\"/>")
                       '(org-protected t))))
       (cond
@@ -1960,7 +1960,7 @@ PUB-DIR is set, use this as the publishing directory."
   "Create image tag with source and attributes."
   (save-match-data
     (if (string-match "^ltxpng/" src)
-       (format "<img src=\"%s\" alt=\"%s\"/>"
+       (format "<img src=\"/%s\" alt=\"/%s\"/>"
                 src (org-find-text-property-in-string 'org-latex-src src))
       (let* ((caption (org-find-text-property-in-string 'org-caption src))
             (attr (org-find-text-property-in-string 'org-attributes src))
@@ -1972,7 +1972,7 @@ PUB-DIR is set, use this as the publishing directory."
 <p>"
                    (if org-par-open "</p>\n" "")
                    (if label (format "id=\"%s\" " (org-solidify-link-text label)) "")))
-       (format "<img src=\"%s\"%s />"
+       (format "<img src=\"/%s\"%s />"
                src
                (if (string-match "\\<alt=" (or attr ""))
                    (concat " " attr )
修改完成以后,就可以在编写Org文档的时候执行 M-x my-screenshot 进行抓屏了,抓好的图片存放在当前目录的 ./image/blog/ 下,命名使用随机命名方式。最后,这个脚本还会开启Emacs Org-Mode 的内嵌图片显示,达到图文并茂的效果。如果不需要该功能,可以使用快捷键 C-c C-x C-v 来关闭。

截个之前配置好的Vim,看看效果吧!

./images/blog/./90530rcx.png

Vim with taglist

Vim的简单配置

| Comments

自己平时喜欢使用GNU Emacs,但是单位工作环境的限制,使我不得不在GNU Screen下运行GNU Emacs。但是貌似Screen和Emacs有点小的兼容性问题,至少我没能调整到完全正常。使用中,Emacs经常会上移一行从而造成画面错乱,让人用的很不爽。为此,我决定在工作环境中暂时使用别的编辑器来替代Emacs。之后,我尝试过使用Micro Emacs,包括Linus最喜欢用的uEmacs PK,可惜这玩意儿和GNU Emacs差异有点大,加之所有MicroEmacs对多字节语系支持地都不是很好,而自己又是个懒人也不是特别Geek,就没继续用下去。于是我重新将目光放到了VIM 上。 好在工作中对编辑器的要求不是特别高,VIM也不是特别难使,自己又磕磕碰碰地在网上找了些配置添加到自己的Vim上。用起来还不错,可以交叉索引跳转,可以Outline,对我来说,这就够了。 下面简单列举一下Vim的配置方法,算个存档记录。

  • Cscope安装配置
    1. 安装Cscope 去http://cscope.sourceforge.net 下载cscope源码,然后编译安装。
    2. 在个人目录下的 .vimrc 文件中添加如下配置
cscope配置
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
43
44
45
46
47
48
49
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" cscope setting
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" if has("cscope")
"   set csprg=/usr/local/bin/cscope
"   set csto=1
"   set cst
"   set nocsverb
"   " add any database in current directory
"   if filereadable("cscope.out")
"       cs add cscope.out
"   endif
"   set csverb
" endif
"
" nmap <C-@>s :cs find s <C-R>=expand("<cword>")<CR><CR>
" nmap <C-@>g :cs find g <C-R>=expand("<cword>")<CR><CR>
" nmap <C-@>c :cs find c <C-R>=expand("<cword>")<CR><CR>
" nmap <C-@>t :cs find t <C-R>=expand("<cword>")<CR><CR>
" nmap <C-@>e :cs find e <C-R>=expand("<cword>")<CR><CR>
" nmap <C-@>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
" nmap <C-@>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
" nmap <C-@>d :cs find d <C-R>=expand("<cword>")<CR><CR>
function! LoadCscope()
let db = findfile("cscope.out", ".;")
if (!empty(db))
  let path = strpart(db, 0, match(db, "/cscope.out$"))
  set nocsverb " suppress 'duplicate connection' error
  set csto=0
  set cst
  " add any database in current directory
  if filereadable(db)
     exe "cs add " . db . " " . path
     nmap <C-c>s :cs find s <C-R>=expand("<cword>")<CR><CR>
     nmap <C-c>g :cs find g <C-R>=expand("<cword>")<CR><CR>
     nmap <C-c>c :cs find c <C-R>=expand("<cword>")<CR><CR>
     nmap <C-c>t :cs find t <C-R>=expand("<cword>")<CR><CR>
     nmap <C-c>e :cs find e <C-R>=expand("<cword>")<CR><CR>
     nmap <C-c>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
     nmap <C-c>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
     nmap <C-c>d :cs find d <C-R>=expand("<cword>")<CR><CR>
  " else add database pointed to by environment
  elseif $CSCOPE_DB != ""
     cs add $CSCOPE_DB
  endif
  set csverb
endif
endfunction
au BufEnter /* call LoadCscope()
  1. 使用时先去要查看的代码目录,然后执行cscope -bqR。完成之后,直接 vi filename 。在需要查找的符号上使用 C-c s/g/c/t/e/f/i/d 即可调用相应的功能。原来cscope插件使用 C-@ 做命令引导键,我为了和Emacs同步,将其改为了 C-c
  2. 添加Outline显示

Outline显示由Taglist来实现:

  1. http://www.vim.org/scripts/script.php?script_id=273 下载Taglist。
  2. 解压后将 taglist.vim 文件放在个人目录下的 .vim/plugin/ 目录中。同时,将解压后的 taglist.txt 文件放在个人目录下的 .vim/doc/ 目录中。
  3. 最后,添加以下配置到个人目录下的 .vimrc 文件中:
taglist配置
1
2
3
4
" use F8 to toggle taglist
nnoremap <silent> <F8> :TlistToggle<CR>
let Tlist_GainFocus_On_ToggleOpen=1
let Tlist_Close_On_Select=1

如此便可方便的通过F8按键打开当前文件的Outline,并将光标置于Outline中,选择条目后自动关闭。

  • 设置高亮搜索,自动缩进以及语法高亮 将以下配置添加到个人目录下的 .vimrc 文件中:
高亮缩进配置
1
2
3
4
5
6
7
8
9
10
set nu
set hlsearch
syntax enable
set showmatch
set ts=4
set softtabstop=4
set shiftwidth=4
set expandtab
set autoindent
set cindent

OK,如此配置后,查看代码编辑代码舒服多了。