介绍如何在Windows下利用AutoHotKey打造接近Awesome窗口管理器的体验。

我从去年开始使用 Awesome 作为桌面管理器,现在已经成了 Awesome 的中毒用户。以往的窗口管理器在快捷键的设置上太过分散,时而要用到 Alt 和最上排的功能键的组合,时而又要用到 Win 键的组合,记忆起来很困难。而 Awesome 的键位设置借鉴了 Vim 的操作方式,记忆起来容易得多。举个例子,切换窗口用的是 Mod4+jMod4+k 两个组合键,比起传统的 Alt+TabAlt+Shift+Tab 而言要好用很多(我相信还有不少用户不知道有 Alt+Shift+Tab 这个东西)。

但这样的好处是有代价的。等我习惯了 Awesome 的快捷键之后,可怕的事情就来了:一旦我需要离开 Linux 到 Windows 下工作,我很难适应原先的快捷键。一个最常犯的错误就是,我总试图用 Ctrl+Mod4+c 来关闭窗口 --bb

既然感觉到别扭,那就要想办法去消除它,否则我很难在 Windows 下愉悦地工作(虽然用 Windows 的机会不多)。但是 Windows 毕竟是一家独大的产物,桌面管理器只有自带的可以选择,甚至连系统快捷键设置界面也欠奉。与之相比,Linux 要自由很多,拥有非常多的桌面环境和窗口管理器可供选择搭配,较为让人熟知的桌面环境就有KDE、GNome、Unity几个派系。

尽管 Windows 不自带修改快捷键的功能,但借助于一个 Windows 下的神器 —— AutoHotKey ,可以实现甚至更加强悍的功能。

autohotkey.png
autohotkey.png
图 1 AutoHotKey AutoHotKey 的体积只有 3M 左右,但是却非常强大。它是如此好用,以至于我在 Linux 下也希望找到类似的替代品。

先列举一下经过定制后我常用的一些窗口快捷键:

快捷键 功能
Win+j 启动窗口切换条并切换到下一个窗口
Win+k 启动窗口切换条并切换到上一个窗口
Win+Enter 运行 cmd.exe
Win+Space 激活光标所在的窗口
Ctrl+Win+t 窗口置顶
Ctrl+Shift+→ 移动窗口到下一个屏幕
Ctrl+Shift+← 移动窗口到上一个屏幕
Ctrl+Alt+j 移动光标到下一个屏幕
Ctrl+Alt+k 移动光标到上一个屏幕

具体实现

窗口切换

用其他键来模拟窗口的切换很简单,AutoHotKey已经实现了 AltTabShiftAltTab 两个动作11注意这两个动作在 Win8 中已经失效。

1
2
LWin & j:: AltTab
LWin & k:: ShiftAltTab

但因为这两个动作封装得太厉害,我无法在这个基础上加入自动光标跟随的功能。为了让我切换窗口后光标跟随到最新激活的窗口,我只好重新实现一个用 Win+Space 触发的动作:

1
2
3
4
#Space::
WinGetPos , , , Width, Height, A
MouseMove, (width/2), (height/2)
return

在使用 Win+jWin+k 切换窗口后,如果需要将光标移动到新的窗口,只需要按下 Win+Space ,则光标将移动到新的窗口的中心。

相比之下,直接在现有的 Alt+Tab 快捷键上添加光标跟随要容易一些:

1
2
3
4
5
6
7
8
9
10
11
12
~!Tab::
KeyWait, Alt
KeyWait, Tab
WinGetPos,x,y,width,height,A
While (x < 0 Or y < 0)
{
Sleep,100
WinGetPos,x,y,width,height,A
IfGreater,A_Index,2,Break
}
MouseMove, (width/2), (height/2)
return

这样,每次我使用 Alt+Tab 切换窗口时,光标会自动移动到最新激活的窗口 2 2Alt+TabWin+j 的功能区分开也有好处,这样我可以根据是否需要光标跟随来决定选择哪种窗口切换方式

窗口置顶

利用 AutoHotKey 的 WinSet 函数可以将窗口置顶。

1
2
3
^#t:: 
WinSet, AlwaysOnTop, Toggle,A
return

多屏幕操作

Awesome的一个很棒的功能就是支持多个屏幕的窗口操作,例如将窗口从一个屏幕移动到另一个屏幕。利用 AutoHotKey 同样也可以实现类似的功能。

对于屏幕间的窗口移动,一个可行的实现方法是先获取该窗口的坐标,如果要移动到上一个屏幕,就将该窗口的x坐标减去当前屏幕的宽度。如果要移动到下一个屏幕,就将该窗口的x坐标加上当前屏幕的宽度。但如果当前窗口处于最大化状态,直接相减可能会出问题,可以采取先恢复到正常大小,移动窗口后再最大化的策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
^#left:: 			; 移动窗口到上一个屏幕
WinGet, mm, MinMax, A ; 判断当前窗口是否为最大化
WinRestore, A ; 先恢复该窗口的大小
WinGetPos, X, Y,,,A ; 获取该窗口的位置
WinMove, A,, X-A_ScreenWidth, Y ; 移动窗口
if(mm = 1){ ; 如果为最大化
WinMaximize, A
}
return

^#right::
WinGet, mm, MinMax, A
WinRestore, A
WinGetPos, X, Y,,,A
WinMove, A,, X+A_ScreenWidth, Y
if(mm = 1){
WinMaximize, A
}
return

光标在多个屏幕间的移动和上面的原理类似。在这个基础上我还添加了一些代码,让光标移动后激活其所在的窗口33本来希望将快捷键绑定到 Ctrl+Win+jCtrl+Win+k 上的,但不知道为什么这两个按键和前面切换窗口的 Win+jWin+k 有冲突,所以只好退而求其次,绑定到了 Ctrl+Alt+jCtrl+Alt+k 上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
^!j::				; 移动光标到上一个屏幕
SysGet, ww, 0 ; 获取当前屏幕宽度
MouseGetPos, X, Y,,,1 ; 获取鼠标位置
MouseMove, X-ww, Y ; 移动鼠标
MouseGetPos, , , id, control ; 获取当前鼠标所在的窗口的id
WinGetTitle, title, ahk_id %id% ; 获取窗口标题
WinActivate, %title% ; 激活该窗口
return

^!k::
SysGet, ww, 0
MouseGetPos, X, Y,,,1
MouseMove, X+ww, Y
MouseGetPos, , , id, control
WinGetTitle, title, ahk_id %id%
WinActivate, %title%
return

总结

目前使用 AutoHotKey 基本实现了 Awesome 常用的一些窗口管理功能。最初我还希望实现 Awesome 的 autofocus 功能,让光标在移动时自动激活其所在的窗口。但要实现这个功能似乎只能通过实现一个循环来持续检测光标的位置。考虑到可能因此带来性能的问题,我只好打消了这个念头。

除此之外,Awesome 还有多标签的特性,类似于虚拟桌面。而 Windows 下同样没有自带虚拟桌面。一个推荐的 workaround 是使用 Dexpot 来添加虚拟桌面的功能,读者可以自行下载体验。

Comments