我一直用的是 FVWM,直到我发现 Sawfish 可以用 lisp 和 Scheme 来配置。这样我可以任意扩展这个 WM 达到我的功能。我可以在里面 使用函数,起动进程,线程,设置变量,环境,continuation,…… 我拥有大量其它人的扩展,我可以修改它们来适应自己的需要。还可 以在使用的同时练习我的 Scheme 和 Common Lisp !太棒了!
Sawfish 很像 Emacs,它的配置语言非常像(实际上就是) elisp,不 过它也可以使用 Scheme 的 lexical binding 和 first class continuation. 你可以看到我在后面有一个例子里使用了Scheme 的 closure。
关于 FVWM,FVWM 是很好的 WM,以前我一直用它。不过我觉得要是 它能用 LISP 或者 Scheme 配置就好了。如果你不想了解 LISP,那 么你可以试试 FVWM。
sawfish-client 里面可以使用类似 Scheme48 的命令,比如 ,open ,in ,quit ... 注意这个 ,quit 是退出 sawfish WM 而不 是 sawfish-client! 小心!,describe 可以得到一个函数的 docstring 帮助。还有 ,apropos, ...
(require sawfish.wm.menus)
- Sawfish 可以设置一个 wm-modifier-value,作为 "W-next" 这样
的热键的 modifier。缺省的设置是 super 键,通常在 PC 键盘上 是 Win 键,我觉得这是很自然的用来操纵“窗口”的热键 modifier。所以以后的设置中如果用到 "W-next" 这样的说法,实 际上就是指 Win 和 PageDown 同时按下。
如果你的 Win 键似乎不起作用,可以把这些内容加到 ~/.sawfish/custom 文件。
(custom-set-typed-variable (quote wm-modifier-value) (quote (super)) (quote modifier-list))
这个问题请参考 这里.
可以一个一个使用 system 调用,但是你一旦忘记写一个 "&" 就会 把整个 WM 挂在那里。所以最好生成新的进程运行程序。
可以做一个表,然后把那些程序和参数放进表里,然后起动这些进程 就行了,就像我这样:
(defun wy-construct-arg-list (str sep)
"Construct a list from a string. Using sep as separetor."
(interactive)
(let* ((result nil)
(add-to-the-list
(lambda (x)
(setq result (cons x result)))))
(let loop ((s "")
(left str)
(ptr 0))
(setq nextchar (substring left 0 1))
(print s)
(catch 'exit
(cond ((and
(string-match "\\s" nextchar)
(> (length s) 0))
(add-to-the-list s)
(setq s ""))
((= (+ 1 ptr) (length str))
(setq s (concat s nextchar))
(if (> (length s) 0) (add-to-the-list s))
(throw 'exit))
(t
(setq s (concat s nextchar))))
(loop s (substring left 1) (+ ptr 1))))
(reverse result)))
;; startup programs
(defun wy-start-entry (prog-entry)
(interactive)
(let ((process (make-process standard-output))
(prog-entry-list (wy-construct-arg-list prog-entry "\\s")))
(apply start-process process (car prog-entry-list) (cdr prog-entry-list))))
(setq startup-programs
'("gnome-panel"
"xset b off"
"xloadimage -onroot -fullscreen /usr/share/backgrounds/images/leafdrops.jpg"
"xsetroot -cursor .sawfish/sawfish.xbm .sawfish/sawfish_mask.xbm"
"xdaliclock"
"xscreensaver -no-splash"
"fcitx"))
(mapc wy-start-up startup-programs)
(defun wy-cleanup-on-exit ()
(mapc stop-process (active-processes)))
(add-hook 'before-exit-hook 'wy-cleanup-on-exit)
我挂了一个 wy-cleanup-on-exit 到 before-exit-hook, 退出时这 些被 wy-start-up 起动的程序都会被关闭。
wy-construct-arg-list 是我自己写的一个函数用来把命令行分解成 一个表。我不知道 rep 有没有现成的函数可以做这个事,所以自己 写了一个。
你可以使用一个叫做 require-try 的函数,如果要用的扩展找不到 就返回 nil. 而不会报错。
(defun require-try (#!rest args)
(let ((result t))
(mapc
(lambda (e)
(condition-case err
(cond ((stringp e) (load-file e))
((symbolp e) (require e))
(t (format standard-output
"Invalid arg to require-try: %s" e)
(setq result nil)))
(file-error
(format standard-output "Couldn't load extension: %s" e)
(setq result nil))))
args)
result))
(setq default-font (get-font "-*-simsun-medium-r-*-*-14-*-*-*-*-*-*-*"))
绑定 "W-Button1-Click1" 到 一个函数,在里面调用 move-window-interactively 就行了。比如下面这个 wy-move-window.
注意必须先打开 sawfish.wm.commands.move-resize, 否则解析不到 move-window-interactively.
(require 'sawfish.wm.commands.move-resize) (defun wy-move-window () (interactive) (move-window-interactively (current-event-window))) (bind-keys window-keymap "W-Button1-Click1" 'wy-move-window)
(bind-keys window-keymap "C-S-Button3-Click1" '(delete-window (current-event-window)))
你可以用一个简单的函数:
(require 'sawfish.wm.util.decode-events)
(defun define-shortcut (win bind)
"Ask for a key and bind that key to select the current
window. Will happily destroy previous binds for that key."
(interactive "%w\ne")
(when win
(let ((key (read-event "Press shortcut key")))
(unless (equal bind key)
(bind-keys global-keymap key `(display-window
,win))))))
(bind-keys window-keymap "M-s" 'define-shortcut)
以后按 M-s 接着再按一个热键就可以标记一个窗口。但是这样有可 能引起很多热键被 WM 截获。对于 Emacs 之类的程序不太好。
所以最好使用 bookmark.jl。 它可以对窗口使用任意热键标记,但 是你必须在这个热键之前执行 bookmark-goto。在bookmark-set 之 后按一个热键,以后 bookmark-goto,然后再按那个热键就可以直接 到达那个窗口了。
bookmark.jl 可以在这里 http://sawfish.skylab.org/WikiSawfishLibrary 找到。
以下是一些简单的配置:
(when (require-try 'bookmark)
(bind-keys global-keymap "W-j" 'bookmark-goto)
(bind-keys global-keymap "W-b" 'bookmark-set)
(bind-keys global-keymap "W-l" 'bookmark-print-list)
(bind-keys global-keymap "W-k" 'bookmark-goto-last)
(setq auto-bookmark-list
'(( "^emacs" . e )
( "^locutus$" . l )
( "^ji$" . j )
( "Galeon$" . g )
( "Mozilla {Build ID: [0-9]+}$" . m)
( "Phoenix" . p )
( "rxvt" . t )))
)
我设计了一个函数,可以知道一个键正在进行什么操作。你只需要按 W-w 然后按你的热键,就能知道它在所有 keymap 里的 binding 和 它们的描述。我把 describe-symbol 和 describe-key 修改成了可 以向 string port 输出,而且修正了一个不能显示复合命令的bug。 从而可以把它们舒服的显示在屏幕上。就像 这样.
下面是函数:
(define (wy-describe-symbol fun #!optional stream)
"Display the documentation of a specified symbol into stream."
(describe-value (symbol-value fun t) fun)
(format (if stream stream standard-output)
"\n%s\n"
(or (documentation fun nil (symbol-value fun t)) "Undocumented.")))
(define (wy-describe-key key #!optional map stream)
"Print key's binding in keymap map, to stream stream."
(require 'rep.lang.doc)
(require 'sawfish.wm.commands.describe)
(let (components)
(letrec
((loop
(lambda (keymap)
(let* ((binding (and key (search-keymap key keymap))))
(when binding
(setq binding (car binding))
(format (or stream standard-output) "\n%s:\n" map)
(setq components (concat components
(and components ? )
(event-name key)))
(cond ((keymapp binding)
(loop binding))
((and (symbolp binding)
(keymapp (symbol-value binding t)))
(loop (symbol-value binding)))
(t
(format (or stream standard-output)
"`%s' is bound to `%s'"
components binding)
(setq command binding)
(while (car command)
(setq command (car command)))
(wy-describe-symbol command stream))))))))
(loop (or map global-keymap)))))
(defun describe-what-am-i-doing ()
"Read a event and search the keymaps for it's definitions. Detailed."
(interactive)
(let ((s (make-string-output-stream))
(key (read-event (concat "Describe key: "))))
(mapc
(lambda (map)
(wy-describe-key key map s))
'(global-keymap
window-keymap
title-keymap
root-window-keymap
border-keymap
close-button-keymap
iconify-button-keymap
maximize-button-keymap
menu-button-keymap
))
(setq msg (get-output-stream-string s))
(if (> (length msg) 0)
(display-message msg
`((background . ,tooltips-background-color)
(foreground . ,tooltips-foreground-color)
(x-justify . left)
(spacing . 2)
(font . ,tooltips-font)))
(display-message (concat
"key `"
(prin1-to-string (event-name key))
"' is not bound"
)))))
(setq tooltips-background-color (get-color "black"))
(setq tooltips-foreground-color (get-color "red"))
(setq tooltips-font (get-font "-*-lucida-medium-r-*-*-14-*-*-*-*-*-*-*"))
(bind-keys global-keymap "W-w" 'describe-what-am-i-doing)
用 display-message 可以在屏幕中央显示字符串,用 display-tooltip 可以在鼠标处显示字符串。
用 rep 的 make-timer 函数就可以。比如:
(require 'rep.io.timers) (make-timer (lambda () (iconify-window (input-focus))) 2)
会在 2 秒之后最小化聚焦窗口。
用 window-snooper 可以得到 sawfish 知道的一切信息。包括窗口 参数,键绑定,窗口属性……
(require 'window-snooper)
(rplacd (cdr window-ops-menu)
`(("Snoop" window-snooper)
,@(cddr window-ops-menu)))
用 iswitch-window 最好。不但可以正则表达式选取窗口,而且可以 用热键对它们进行 iconify, shade, ... 等操作。
具体可以的操作有这些:
;; C-s,A-s,TAB find next matching window ;; C-r,A-r,M-TAB find previous matching window ;; C-g,ESC quit waffle ;; C-u clear input buffer ;; backspace delete previous character ;; C-z,A-z iconify window ;; C-h,A-h shade window ;; RET select window
iswitch-window 可以方便的寻找到窗口,但是你总是需要按一下RET 才能到达。如果你只是想快速的轮换窗口,sawfish 有一个函数叫 cycle-windows,把它绑定到一个键就行了。
比如我绑定到了 W-next:
(bind-keys global-keymap "W-Next" 'cycle-windows)
因为我的 Emacs 用 C-next 轮换 buffer, Sawfish 用 W-next 当然很自然了。
改变窗口的层就行了。这里有3个函数可以帮助你设置窗口的层:
wy-raise-window-layer 可以帮你把窗口放到更上面一层, wy-lower-window-layer 可以帮你把窗口放到下面一层, wy-reset-window-layer 可以把窗口放到第 0 层。
(defun wy-raise-window-layer (window)
(interactive "%w")
(let ((layer (1+ (window-get window 'depth))))
(display-message (concat
"Layer "
(prin1-to-string layer)))
(set-window-depth window layer)))
(defun wy-lower-window-layer (window)
(interactive "%w")
(let ((layer (1- (window-get window 'depth))))
(display-message (concat
"Layer "
(prin1-to-string layer)))
(set-window-depth window layer)))
(defun wy-reset-window-layer (window)
(interactive "%w")
(let ((layer 0))
(display-message (concat
"Layer "
(prin1-to-string layer)))
(set-window-depth window layer)))
(bind-keys window-keymap "W-KP_Add" 'wy-raise-window-layer)
(bind-keys window-keymap "W-KP_Subtract" 'wy-lower-window-layer)
(bind-keys window-keymap "W-=" 'wy-reset-window-layer)
使用 system 时一定要小心,如果用 X 的程序,后面一定要加 "&" 让它在后台运行,否则 WM 就会被挂起,键盘会被锁定。有时你不得 不重新起动。
为了避免这种情况,让程序的起动更加流畅。你可以参考这个函数:
(defun wy-run (cmd)
"Run a command in a new process. And let it start in
background. Without stop the current WM execution"
(interactive)
(system
(if (string-match ".*&\\s*$" cmd)
cmd
(concat cmd " &"))))
我们用 (wy-run "程序") 代替 (system "程序")。这样每次起动程 序时,会首先起动一个新的线程。在这个线程里调用 system 起动那 个程序。如果命令行最后没有 "&", 我们自己给它加一个 "&". 这样 就不会挂起我们的 WM 了。
用 sawfish-spell 就行了。随便在那个窗口选中一些字符然后按 C-W-c 就行了。
(when (require-try 'sawfish-spell)
(bind-keys global-keymap
"C-W-c" spellcheck-interactive))
你可以使用 shrink-windows-to-fit 扩展。绑定4个键到4个方向缩 小的函数就行了。Magic!
(when (require-try 'shrink-windows-to-fit) (bind-keys window-keymap "M-W-Left" 'shrink-window-left) (bind-keys window-keymap "M-W-Right" 'shrink-window-right) (bind-keys window-keymap "M-W-Up" 'shrink-window-up) (bind-keys window-keymap "M-W-Down" 'shrink-window-down) )
只要定义一个菜单,把它绑定到一个键上就行了。注意,必须先加载 sawfish.wm.menus 才能使用 popup-menu 函数。
(setq menu-program-stays-running t)
(require 'sawfish.wm.menus)
(setq my-menu
'(("screen" (wy-run "rxvt -e screen -xRR &"))
("rxvt" (wy-run "rxvt &"))
("Emacs" (wy-run "emacs &"))
("Gthumb" (wy-run "gthumb &"))
("Phoenix" (wy-run "phoenix &"))
("gv" (wy-run "gv &"))
))
(defun popup-my-menu ()
(interactive)
(popup-menu my-menu))
(bind-keys global-keymap "W-Button1-Click1" 'popup-my-menu)
(bind-keys global-keymap "W-Button2-Click1" 'popup-root-menu)
(bind-keys global-keymap "W-Button3-Click1" 'popup-window-menu)
(bind-keys window-keymap "W-Button1-Click1" 'popup-my-menu)
(bind-keys window-keymap "W-Button2-Click1" 'popup-root-menu)
(bind-keys window-keymap "W-Button3-Click1" 'popup-window-menu)
(bind-keys window-keymap "W-Insert" 'insert-workspace-after)
我自定义了一些函数。如果你上次切换了窗口,那么按 M-` 就可以 回到上一个窗口,如果你上次切换了 workspace,那么 M-` 就回到 上一个工作区。
;;; switch to last workspace or window
(define window-or-workspace? 'window)
(add-hook 'leave-workspace-hook
(lambda (current)
(setq workspace-last current)
(setq window-or-workspace? 'workspace)))
(add-hook 'focus-out-hook
(lambda (current)
(setq window-last current)
(setq window-or-workspace? 'window)))
(bind-keys global-keymap
"W-`"
(lambda ()
(if (eq window-or-workspace? 'workspace)
(select-workspace workspace-last)
(display-window window-last))))
(defun shove-window (dir &optional no-focus)
"Move focused window 'left, 'right, 'up or 'down to screen edges."
(interactive)
(let* ((win (input-focus))
(pos (window-position win))
(dim (window-frame-dimensions win))
(endx (car pos))
(endy (cdr pos)))
(cond ((eq dir 'left) (setq endx 0))
((eq dir 'right) (setq endx (- (screen-width) (car dim))))
((eq dir 'up) (setq endy 0))
((eq dir 'down) (setq endy (- (screen-height) (cdr dim)))))
(move-window-to win endx endy)
(unless no-focus
(display-window win))))
(bind-keys global-keymap "C-M-Left" '(shove-window 'left))
(bind-keys global-keymap "C-M-Right" '(shove-window 'right))
(bind-keys global-keymap "C-M-Up" '(shove-window 'up))
(bind-keys global-keymap "C-M-Down" '(shove-window 'down))
先做一些包装函数,然后绑定到键上:
(defun wy-shade-window (win) (interactive "%w") (shade-window win)) (defun wy-unshade-window (win) (interactive "%w") (unshade-window win)) (defun wy-iconify-window (win) (interactive "%w") (iconify-window win) (setq last-iconified-window win)) (defun wy-uniconify-window () (interactive) (uniconify-window last-iconified-window) (display-window last-iconified-window)) (bind-keys window-keymap "W-Up" 'wy-shade-window) (bind-keys window-keymap "W-Down" 'wy-unshade-window) (bind-keys window-keymap "C-W-Down" 'wy-iconify-window) (bind-keys window-keymap "C-W-Up" 'wy-uniconify-window)
(defun wy-shade-window (win) (interactive "%w") (shade-window win)) (defun wy-unshade-window (win) (interactive "%w") (unshade-window win)) (defun wy-iconify-window (win) (interactive "%w") (iconify-window win) (setq last-iconified-window win)) (defun wy-uniconify-window () (interactive) (uniconify-window last-iconified-window) (display-window last-iconified-window)) (bind-keys window-keymap "W-Up" 'wy-shade-window) (bind-keys window-keymap "W-Down" 'wy-unshade-window) (bind-keys window-keymap "C-W-Down" 'wy-iconify-window) (bind-keys window-keymap "C-W-Up" 'wy-uniconify-window)
(require 'sawfish.wm.focus)
(defun wy-warp-to (x y)
(interactive)
(let* ((win (input-focus))
(xpix (floor (inexact->exact (* x (car (window-dimensions win))))))
(ypix (floor (inexact->exact (* y (cdr (window-dimensions win)))))))
(warp-cursor-to-window win xpix ypix)))
(setq warp-to-window-enabled t)
(bind-keys global-keymap "W-c" '(wy-warp-to 0.5 0.5))
注意,你必须设置 warp-to-window-enabled 为 t.
可以使用一个扩展叫做 animate-move.
(when (require-try 'animate-move)
(bind-keys window-keymap
"M-W-s" 'toggle-window-shaded
"M-W-i" 'iconify-window
"M-W-c" 'animate-center-window
"M-W-k" 'delete-window
"M-W-r" 'rotate-move))
使用 set-frame-style 可以改变窗口边框风格。比如
(set-frame-style (get-window-by-name-re "rxvt") 'microGUI)
可以把名字匹配 "rxvt" 的窗口,都设置成microGUI 的风格。
(find-all-frame-styles)
可以得到一个所有 frame-style 的 list. style 就是所谓“主题”。
这个你需要用 set-window-type 修改窗口的 window-type 属性。比 如,
(set-window-type (get-window-by-name-re "[Dd]ali") 'unframed)
可以把 xdaliclock 的窗口设置为不加标题和边框。另外还有很多 window-type:
比如“把焦点移动到右边的窗口”?用一个扩展叫做 focus-by-direction 就行了:
(require-try 'focus-by-direction) (bind-keys window-keymap "C-W-KP_Up" 'focus-north-warp) (bind-keys window-keymap "C-W-KP_Left" 'focus-west-warp) (bind-keys window-keymap "C-W-KP_Right" 'focus-east-warp) (bind-keys window-keymap "C-W-KP_Down" 'focus-south-warp)
使用 undo 扩展就可以了。几乎所有窗口操作,比如最大化,最小化, shade, 改变大小,移动,……都可以 undo.
(defun capture-root-window ()
(interactive)
(make-thread
(lambda ()
((system "import -window root shot.png")
(system "display shot.png&")))))
(defun capture-this-window ()
(interactive)
(let ((w (current-event-window)))
(when w
(make-thread
(lambda ()
(display-message (concat
"import -window "
(prin1-to-string (window-id w)) " window.png"))
(system (concat "import -window "
(prin1-to-string (window-id w)) " window.png"))
(system "display window.png&"))))))
(defun capture-region ()
(interactive)
(let
((process (make-process standard-output)))
(start-process process "import" "capture.png")))
(bind-keys global-keymap "F7" 'capture-root-window)
(bind-keys window-keymap "W-F7" 'capture-this-window)
(bind-keys window-keymap "M-F7" 'capture-region)
(bind-keys window-keymap "C-S-Button3-Click1" '(delete-window (current-event-window)))
(bind-keys window-keymap "W-F9" 'run-shell-command)
;; short cuts for xmms (bind-keys global-keymap "Pause" '(wy-run "xmms -u&")) (bind-keys global-keymap "W-KP_Left" '(wy-run "xmms -r&")) (bind-keys global-keymap "W-KP_Right" '(wy-run "xmms -f &")) (bind-keys global-keymap "W-KP_Insert" '(wy-run "xmms -p &")) (bind-keys global-keymap "W-KP_Delete" '(wy-run "xmms -s &")) (bind-keys global-keymap "W-KP_Enter" '(wy-run "xmms -u &"))
设计一些函数就可以完成这个功能。不过这些函数还有更多的功能。 不但可以在 rxvt 之间轮转,而且可以在任意条件的窗口之间轮转。
(defun name-match-p (win name)
(string-match name (window-name win)))
(defun class-match-p (win class)
(string-match class (nth 2 (get-x-property win 'WM_CLASS))))
(defun rotate-around (elem lst)
(if elem
(append (cdr (memq elem lst)) (reverse (memq elem (reverse lst))))
lst))
(defun rotate-list-left (lst)
(append (cdr lst) (list (car lst))))
(define next-window-by
(let ((my-windows (window-order current-workspace)))
(lambda (pred repeating? . args)
(if repeating?
(setq my-windows (rotate-list-left my-windows))
(setq my-windows (window-order current-workspace)))
(catch 'foo
(mapc (lambda (w)
(when (apply pred w args)
(throw 'foo w)))
(rotate-around (input-focus) my-windows))
nil))))
(defun focus-next (pred &rest arg)
(let ((repeating? (eq (car last-command) 'focus-next)))
(let ((win
(if repeating?
(apply next-window-by pred t arg)
(apply next-window-by pred nil arg))))
(display-window win))))
(bind-keys global-keymap "H-e" '(focus-next class-match-p t "emacs"))
(bind-keys global-keymap "H-M-r" '(focus-next class-match-p nil "rxvt|xterm"))
(bind-keys global-keymap "H-r" '(focus-next class-match-p t "rxvt|XTerm"))
(bind-keys global-keymap "H-m" '(focus-next class-match-p t "XMMS"))
(bind-keys global-keymap "H-x" '(focus-next name-match-p t "xv|ImageMagick"))
最后两句可以使得每按一下 H-r 就跳到下一个 rxvt, 每按一下 H-e 就跳到下一个 emacs. 每按一下 H-M-r 就跳到下一个 rxvt 或者 xterm, 而且可以跨过 workspace 轮转。
(bind-keys global-keymap
"C-Pause" '(system "xscreensaver-command -activate&"))
(bind-keys global-keymap "C-Break"
'(system "xscreensaver-command -lock &"))
(bind-keys global-keymap "M-Pause"
'(system "xscreensaver-command -lock & (sleep 2; xset dpms force off) &"))
以上的设定是:C-Pause 起动屏幕保护, C-Break 起动屏保锁定屏 幕,M-Pause 起动屏保,锁定屏幕,并且关闭显示器。
下面这个设置使用了 jump-or-exec。按下 W-e 时:
(when (require-try 'jump-or-exec)
;; load emacs
;; or switch to it
;; and toggle my notes buffer (bound to F1) if already focused
(bind-keys global-keymap
"W-e" `(jump-or-exec "^emacs@"
,(lambda ()
(display-message "emacs loading...")
(wy-run "emacs &"))
;; f2 is bound to toggle between the last two buffers
,(lambda (wind)
(synthesize-event "F12" wind)))))
可以。因为 librep 支持 Scheme. 你可以使用 lexical binding 和 first class continuation. 举一个例子:
(define con t) (display-message (call/cc (lambda (ok) (define con ok) "kick"))) (con "haha")
(defun get-window-by-class-re (re)
"Get a window by matching against its class"
(let ((windows (window-order))
w done match)
(while (and windows (not done))
(setq w (car windows))
(setq windows (cdr windows))
(when (string-match re (window-class w))
(setq done t)
(setq match w)))
(when match match)))
(defun name-match-p (win name) (string-match name (window-name win))) (defun class-match-p (win class) (string-match class (nth 2 (get-x-property win 'WM_CLASS))))
有了这两个判断函数我们可以定义一些函数可以跳转,或者操纵那个 满足条件的窗口。
(defun rotate-around (elem lst)
(if elem
(append (cdr (memq elem lst)) (reverse (memq elem (reverse lst))))
lst))
(defun rotate-list-left (lst)
(append (cdr lst) (list (car lst))))
(define next-window-by
(let ((my-windows (window-order current-workspace)))
(lambda (pred repeating? . args)
(if repeating?
(setq my-windows (rotate-list-left my-windows))
(setq my-windows (window-order current-workspace)))
(catch 'foo
(mapc (lambda (w)
(when (apply pred w args)
(throw 'foo w)))
(rotate-around (input-focus) my-windows))
nil))))
(defun focus-next (pred &rest arg)
(let ((repeating? (eq (car last-command) 'focus-next)))
(let ((win
(if repeating?
(apply next-window-by pred t arg)
(apply next-window-by pred nil arg))))
(display-window win))))
比如,绑定 F4 可以在 rxvt 之间循环:
(bind-keys global-keymap "F4" '(focus-next class-match-p "rxvt"))
(defun wy-move-window () (interactive) (move-window-interactively (current-event-window)))
(defun wy-warp-to (x y)
(interactive)
(let* ((win (input-focus))
(xpix (floor (inexact->exact (* x (car (window-dimensions win))))))
(ypix (floor (inexact->exact (* y (cdr (window-dimensions win)))))))
(warp-cursor-to-window win xpix ypix)))
比如我们可以绑定:
(bind-keys global-keymap "W-w" '(wy-warp-to 0.5 0.5))
这样鼠标不管在哪里,一按 W-w, 它就到窗口中央。