使用Devil’s Pie为窗口定制启动规则

很长一段时间里,我有一个疑惑:

当我启动系统后,我习惯做以下事:打开终端更新系统及程序组件、打开浏览器准备阅读Google Reader、打开GajimKmess、打开ThunderBird以 接收电邮;但是如果我做完一件事再打开另一个程序,则由于程序启动需要时间和网速较慢的原因,消耗在等待上的时间往往很长;如果我在终端中做更新的时候启 动别的程序,则启动较慢的浏览器会挡住我在终端中的输入、而Gajim又会和Kmess重叠在一起、Thunderbird又会覆盖住所有窗口;于是,我 必须切换回终端才能继续被中断的输入、必须把Thunderbird移动到其它Workspace才能继续在浏览器中查看Google Reader……

太麻烦了。如果能在启动终端的时候使用热键启动浏览器并使之在另一个Workspace打开、使Gajim和Kmess在其它Workspace打 开并排列在不同的位置,那么我在当前Workspace中终端里的输入就不会被打断,而当我做完这件事儿后再切换到其它Workspace时,浏览器已经 打开、电邮已经收完,所有接下来要做的事都已准备停当,那些时间就不会被浪费。

Devil’s Pie就是可以解决这个疑惑的工具。

确切地说,Devil’s Pie是一个窗口匹配程序,它允许用户为特定的应用程序定制启动规则。当程序启动并创建窗口时,Devil’s Pie会查询用户已定制的规则,如果遇到与该窗口匹配的规则,则将其应用到该窗口,例如在某一Workspace打开窗口、在Workspace的某一位 置放置窗口、在各个Workspace均显示该窗口、折叠窗口、设置窗口层次等。

在接下的几天里,我将逐步介绍一下此工具的用法。




Devil’s Pie的工作原理是这样的:当用户通过devilspie命令启动它的时候,它会读取用户主目录下的”.devilspie”目录中所有以”.ds”为后 缀的文件,这些文件即是用户为特定的应用程序定制的应用规则;然后Devil’s Pie会按照这些规则去匹配当前所有桌面上的所有窗口,如果发现匹配项,则将规则应用到该窗口;此后,运行在后台的Devil’s Pie会检查所有新启动的窗口,一旦发现有与当前的规则中匹配的就将相应的规则应用到该窗口。

Devil’s Pie(以后简称DP)的规则文件采用S-expression语法,此语法的逻辑很简单,它使用分号”;”标记一行注释,使用小括号标记一个代码块。此语法在结构上分为五类,分别是”流程控制“、”布尔表达式“、”字符串测试“、”匹配器“和”动作“。

“动作”即是DP对匹配规则的窗口应用的指令。”动作”可以单独使用,下面我们来写第一个DP规则。首先,在”~/.devilspie”目录(如果没有就新建)中新建一个名为”debug.ds”的文件,在此文件中只填写如下内容:

debug

保存后退出,然后在虚拟终端中输入”devilspie”启动DP,它会在终端中输出当前运行的所有窗口的一些特征值,如果有新窗口被打开,DP会将它们的特征值打印到终端。这些特征值格式如下:

Window Title: ‘FbPager’; Application Name: ‘FbPager’; Class: ‘FbPager’; Geometry: 521×66+0+0

直接在规则文件中书写的动作指令会被应用到所有窗口,当然,一般情况下,我们不愿如此。因此就要告诉DP将这些指令应用到哪些窗口,这时就要用到上面所示的窗口特征值来判断了。

这些特征值包括:

Window Title:窗口的标题,对应DP语法的匹配器”window_name“:

String window_name()

Application Name:程序的名字,对应DP语法的匹配器”application_name“:

String application_name()

Class:窗口的类名,对应DP语法的匹配器”window_class“:

String window_class()

Geometry:窗口的几何属性,没有对应的匹配器。

此外,DP还有以下几个匹配器:

window_role

String window_role()

此匹配器返回窗口的角色名,该属性由窗口内部的 WM_WINDOW_ROLE 变量定义。用户可以在规则文件中填写如下内容来获得目标窗口的角色名:

(print (window_role))

window_workspace

int window_workspace()

此匹配器返回窗口所在的工作区对应的一个整数。

window_xid

int window_xid()

此匹配器返回窗口的xid值,用户可以在debug.ds文件中填写如下内容来获得窗口的xid值:

(print (window_xid))

window_property

String window_property(String propertyname)

此匹配器需要一个参数,此参数是窗口的一个属性名,此匹配器返回该属性名对应的属性值。你可以在这里找到详细的窗口属性名列表。

今天我主要介绍了一下Devil’s Pie的工作原理和它的一些匹配器,明天我将谈一下如何使用DP的这些匹配器。






昨天我介绍了Devil’s Pie的匹配器,也提到,DP通过对窗口应用动作指令来控制窗口的行为,而如果在不给出条件的情况下在DP的规则文件中填写动作指令,则所有的窗口都会被影响到。当然,我们一般不会这样做,而是希望对特定的窗口制定特定的规则,这就需要用匹配器来为动作指令添加条件。

不过首先要介绍一下DP的流程控制结构,先看一个DP的配置文件:

;如果启动的程序是FbPager,将其置于底层
(if
(is(window_class)"FbPager")
(below)
)

此规则的用途已在注释中标明,这里就不多说了。此规则是一个最简单的DP规则,不过它显示了DP规则语法的完整结构:

一个DP规则由一对圆括号包围,结构伪码为:

(if
条件
动作指令
)

条件由一对圆括号包围,动作也由一对圆括号包围。其中,条件是由字符串测试语句组成。

字符串测试语句分三种:is、contains和matches。此语句的结构伪码为:

字符串测试指令(匹配器)”字符串”

工作原理为:当一个窗口启动的时候,DP的匹配器会返回相应的字符串,如果匹配器返回的字符串与后面给定的字符串相同或匹配,则条件成立,DP将下面的动作指令应用到该窗口。

“is”表示条件只有在匹配器返回的字符串与给出的字符串完全相同的情况下条件才能成立;”contains”表示只有在匹配器返回的字符串中包含给出的字符串时条件才能成立;”matches”表示条件只有在匹配器返回的字符串符合后面给出的正则表达式时才成立。例如:

is(window_name)”terminal”

对于窗口名为”terminal”的窗口,条件成立。再如:

contains(window_name)”terminal”

对于窗口名为”urxvt terminal”、”terminal2008″或”terminal”等都成立。而

matches(window_name)”^[a-zA-Z]+$”

对于所有窗口名只由英文字母组成的窗口,条件成立。

今天主要介绍了怎样使用字符串测试语句作为动作指令激活的条件,以及怎样使用DP的流程控制结构来组织条件和动作指令成为一条正确的DP规则。明天,我将介绍怎样编写更复杂的DP规则以及全部可用的DP动作指令。最后,再看一个简单的DP规则:

;可匿名访问网络的代理工具Your Freedom在启动之后会弹出一个对话框,很烦人,此规则将该对话框自动在第四个workspace打开

(if
(matches(window_name)"^Your Freedom /d{4}//d{2}//d{2} /d{2}:/d{2}:/d{2} [am|pm]$")
(setworkspace 4)
)





通过DP的简单规则已经可以对特定窗口做出一定的处理了,不过很多时候我们需要的不仅限于此。例如对于我开机必启动的Your Freedom, 我只是需要启动它而已,并不想看到它的界面,所以我想把它折叠起来;我不希望它出现在我当前正在工作的Workspace,所以我要把它放在一个冷清的工 作区;它启动后往往会弹出一个提示窗口,我希望把它也挪走并最小化到任务栏。那么此时,简单的DP规则已显得捉襟见肘。

高级的DP规则包括两个部分,一是多条件的联合判断和多动作指令的并发调用,二是语法结构的嵌套使用。

首先看多条件的联合判断。

大部分时候,我们通过一个条件就可以判断出要执行指令的窗口,但是有时候,我们希望这个条件更严谨一些。

例如对于支持Google Talk的即时通信工具Psi,我可以通过设置“is(window_class)”psi””一个条件来将它在启动时即移动到第四个工作区;但是一旦有 新的消息,它弹出的对话窗口的“window_class”也是“psi”,那么这些对话窗口就不会像我希望的那样出现在我当前的工作区,而是也被放置在 第四个工作区中。不过Psi主窗口和对话窗口的其它属性还是不一样的,下面是Psi主窗口的属性:

Window Title: ‘Psi’; Application Name: ‘Psi’; Class: ‘psi’;

下面是对话窗口的属性:

Window Title: ‘lenin.lee’; Application Name: ‘psi’; Class: ‘psi’;

可以看到,它们的窗口标题和程序名称都不一样。所以,下面的条件就可以区分Psi主窗口和它的对话窗口了:

(if
(and
(is(window_name)"Psi")
(is(application_name)"Psi")
(is(window_class)"psi")
)
(set_workspace 4)
)

此规则中的条件部分即:

(and
(is(window_name)"Psi")
(is(application_name)"Psi")
(is(window_class)"psi")
)

无论条件怎么写,只要包围条件的圆括号中的逻辑表达式成立(为真),DP即对该窗口应用动作指令。DP的逻辑结构有三种,即“与”(and)、“或 ”(or)和“非”(not)。上面的条件就是与表达式,它表示只有下面的三个条件都成立,整个联合条件才成立。如果把上面的“and”换成“or”,则 是或表达式,它表示只要三个条件中成立一个,整个联合条件就成立。而非表达式表示只有所有的条件都不成立,整个联合条件才返回真。

相对来说,多动作的并发调用则简单得多。下面是两个动作并发执行的规则代码:

(if
(and
(is(window_name)"Psi")
(is(application_name)"Psi")
(is(window_class)"psi")
)
(begin
(set_workspace 4)
(above)
)
)

在此规则中,不但要将Psi主窗口置于第四个工作,还要把它放在所有窗口之上。

和Psi一样,很多时候一个程序并非只有一个窗口,我们如果要对一个程序的所有窗口分别定制不同的规则,就得将每个窗口的规则保存到一个配置文件 中,这样不直观,也不利于管理。但是,我们也可以通过DP语法结构的嵌套来将一个程序的所有窗口规则写到一个文件中。下面是我对vlc编写的配置文件:

(begin
;如果是VLC控制面板,置于第七个工作区的右下角
(if
(is(window_name)"VLC media player")
(begin
(set_workspace 7)
(geometry "-0-0")
)
)
;如果是VLC的播放窗口,置为所有工作区可见、置于所有窗口之上、屏幕右下角
(if
(and
(contains(window_name)"VLC")
(contains(window_name)"XVideo output")
)
(begin
(pin)
(above)
(geometry "-0-0")
)
)
)

像多动作并发调用那样,使用一对带有“begin”的圆括号将两个窗口规则包围起来就可以了。

今天我介绍了DP高级规则的编写,至此,你已经可以随意为任何窗口程序定制自己的规则了。明天我将把剩余的DP动作指令归纳出来。最后,解决文章开头关于Your Freedom问题的规则如下:

(begin
(if
(and
(matches(window_name)"^Your Freedom$")
(is(application_name)"Your Freedom")
(is(window_class)"emsgui")
)
(begin
(set_workspace 5)
(shade)
(below)
)
)

(if
(and
(matches(window_name)"^Your Freedom /d{4}//d{2}//d{2} /d{2}:/d{2}:/d{2} [am|pm]$")
(is(application_name)"Your Freedom")
(is(window_class)"emsgui")
)
(begin
(set_workspace 5)
(minimize)
)
)
)









debug

输出当前正在运行的所有窗口的属性,如果有新的窗口被打开,它们的属性也会被输出到终端中。

使用方法:

(debug)

print

输出字符串到终端,没有换行符。

使用方法:

(print hello world)

println

输出字符串到终端,有换行符。

使用方法:

(println hello world)

str

函数原型是:

String str(Object value)

将value转换成字符串并返回,使用方法:

(str value)

hex

函数原型是:

String hex(int value)

它将一个整形值转换成十六进制数的字符串并返回之。

使用方法:

(hex 12345)

geometry

函数原型为:

void geometry(String geo)

此指令需要一个geometry格式的字符串作为参数,此字符串规定窗口的大小和位置。

使用方法:

(geometry “640×480+0-20″)

fullscreen

此指令不需要任何参数,它将窗口全屏化。

使用方法:

(fullscreen)

focus

被应用该指令的窗口会被提升至前台,光标焦点会聚焦到该窗口。

使用方法:

(focus)

center

将窗口置于屏幕中央。使用方法同上。

maximize

将窗口最大化。

maximize_vertically

纵向最大化窗口。

maximize_horizontally

横向最大化窗口。

unmaximize

恢复最大化。

minimize

最小化窗口。

unminimize

恢复最小化窗口。

shade

折叠窗口。

unshade

恢复折叠窗口。

close

关闭窗口。

pin

将窗口置为所有工作区可见。

unpin

取消窗口置于所有工作区。

stick

在我的Fluxbox下和pin效果是相同的,不太清楚它们之间有什么区别。

unstick

和unpin一样。

set_workspace

置窗口于某一个工作区。

使用方法:

(set_workspace 4)

set_viewport

在我这里和set_workspace效果一样。

skip_pager

终止窗口在pager里面显示。

使用方法:

(skip_pager)

skip_tasklist

阻止窗口显示在任务栏列表中。

使用方法:

(skip_tasklist)

above

置窗口于所有窗口之上。

使用方法:

(above)

below

置窗口于所有窗口之下。

使用方法:

(below)

undecorate

去掉窗口管理器对窗口的装饰,可以理解为去掉窗口标题栏和边框,这个很有意思。

(undecorate)

wintype

函数原型为:

void wintype(String type)

改变窗口类型,这些类型可以是以下几个中的一个:

“normal”, “dialog”, “menu”, “toolbar”, “splashscreen”, “utility”, “dock”, “desktop”.

使用方法:

(wintype “dialog”)

opacity

设置窗口的透明度。

使用方法:

;透明度60%
(opacity 60)

spawn_sync

函数原型为:

String spawnsync(String command)
String spawn
sync(String[] command)

该指令可以跟一条或多条shell命令,将它或它们在前台运行并返回其输出结果。

使用方法:

(spawnsync command)
(spawn
sync command1 command2 …)

spawn_async

与spawn_sync不同的是,它将命令在后台运行。

好了,至此,对Devil’s Pie的介绍已经完全结束。通过对这个工具的灵活运用,相信我们可以创造一个真正属于自己的完美桌面。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值