LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 5314|回复: 30

Sawfish 窗口管理器的一些东西

[复制链接]
发表于 2006-10-9 00:00:58 | 显示全部楼层 |阅读模式
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  实时控制 Sawfish
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


=======================================================
    使用 sawfish-client
=======================================================


sawfish-client 可以对 Sawfish 进行实时控制,可以直接在终端里面运行
sawfish-client ,不过更好用的方法是使用 sawfish.el 在  Emacs  里的
sawfish-mode 里面进行编辑,并用 C-x C-e 来执行相应的 lisp 语句。

值得注意的是,sawfish-client 里面有一个 (quit) 函数,它并不是退出
sawfish-client ,而是直接退出 Sawfish ,随便执行这个操作有可能造成你其
他 X 程序的数据丢失。


=======================================================
    使用 sawfish-ui
=======================================================


sawfish-ui 是一个图形化的配置对话框,里面可以配置包括主题、快捷键等很
多东西,配置会被写入到 ~/.sawfish/custom 里面去。



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  使用扩展
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


使用 Sawfish 的扩展和  Emacs  差不多,也是通过一个 load-path 来加载扩展。
我把下载到的扩展全部放在 ~/.sawfish/extensions 里面,并在 ~/.sawfishrc
里面加入一句


  1. (setq load-path (cons "~/.sawfish/extensions"
  2.                       load-path))
复制代码


然后就可以通过 require 来加载对应的扩展了。在这里可以找到许多下
载扩展的地方。这里介绍几个常用的扩展。

=======================================================
    jump-or-exec
=======================================================


自从我第一次接触这个功能我就再也离不开它了,它让我保持某个程序只有一个
实例,并且能方便地切换。正如它的名字所言,jump 对应于跳转,如果程序正
在运行,则跳转到程序那里,即把程序的窗口切换到前台,这个功能让我不再需
要使用任务栏来切换窗口;exec 表示运行,如果程序没有运行,着运行它,通
常,新运行的程序会自动被带到前台,这样,不管程序有没有在运行,我只需要
一个快捷键就可以把它唤出,于是我根本不需要查看任务栏得出当前哪个程序有
没有在运行,因为我不再需要关心这个问题。这样,由于任务栏的两大存在理由
都已经没有意义,jump-or-exec 的出现让我最终。

这里可以下载到 jump-or-exec.jl ,在 Sawfish Wiki 上有一个例子讲解如
何配置。我建立了一个列表,把我常用的只需要启动一个实例的程序都配置为
jump-or-exec :


  1. ;;; jump-or-exec
  2. (require 'jump-or-exec)

  3. (defun jump-or-exec-emacs ()
  4.   (interactive)
  5.   (jump-or-exec "emacs@"                 ; Emacs's title
  6.                 (lambda ()                 ; When Emacs isn't running
  7.                   (kid-display-message "starting Emacs ...")
  8.                   (system "emacs &")
  9.                   "sleep 10")
  10.                 (lambda (wind)                ; When already focused
  11.                   (display-window wind))))

  12. (defvar kid-jump-or-exec-list
  13.   '(("H-q" "QTerm" "qterm &")
  14.     ("H-f" "Firefox" "firefox &")
  15.     ("H-t" "^screen$" "urxvt -e screen -xRRS urxvt &")
  16.     ("H-g" "gnus@" "emacs --eval \'(setq frame-title-format "gnus@%b")\' -f gnus &")))
复制代码


其中 kid-display-message 是我自己定义的一个工具函数,可以在找到。

=======================================================
    更加智能的 emacsclient
=======================================================


Emacs  启动比较慢,一直以来有 FasterEmacs , Emacs  自己提供的一个解决办法
是,这个方法的缺点是把要打开的文件发送给
Emacs  之后, Emacs  的主窗口并不能自动切换到前台,这个缺点可以通过窗口管
理器来解决,这里介绍使用 sawfish-client 的方法:

首先,在 ~/.emacs 里面加入 (server-start) 好让  Emacs  在启动的时候启动
server ,这样就可以使用emacsclient 来连接到  Emacs  了。然后在 Sawfish 里
面写一个切换到  Emacs  的函数,我这里使用了 ,虽然这不是必须
的,但是我平时也使用这个功能来切换到  Emacs  。


  1. (defun jump-or-exec-emacs ()
  2.   (interactive)
  3.   (jump-or-exec "emacs@"                 ; Emacs's title
  4.                 (lambda ()                 ; When Emacs isn't running
  5.                   (system "emacs &"))
  6.                 (lambda (wind)                ; When already focused
  7.                   (display-window wind))))

  8. (bind-keys global-keymap
  9.            "H-e" 'jump-or-exec-emacs)
复制代码


然后我可以写一个脚本来调用 emacsclient:


  1. #!/bin/sh

  2. # first jump-or-exec to emacs
  3. sawfish-client -c jump-or-exec-emacs

  4. # then call emacsclient
  5. emacsclient "$@"
复制代码


不过这样并不能满足要求,因为  Emacs  的启动需要几秒钟的时间,如果事先没
有启动  Emacs  ,就调用 e ,则 sawfis-client -c jump-or-exec-emacs 立即
返回,这个时候  Emacs  还没有完全启动起来并执行 (server-start) ,所以
emacsclient 这个时候会抱怨找不到 server 。没关系,我们修改一下代码,如
果  Emacs  没有启动起来,就 sleep 几秒,在 Sawfish 里面 sleep 当然不好,
所以将作为一个返回值传回脚本 ,并由脚本来 sleep 。下面是修改后的
jump-or-exec-emacs 函数:


  1. (defun jump-or-exec-emacs ()
  2.   (interactive)
  3.   (jump-or-exec "emacs@"                 ; Emacs's title
  4.                 (lambda ()                 ; When Emacs isn't running
  5.                   (system "emacs &")
  6.                   "sleep 3")
  7.                 (lambda (wind)                ; When already focused
  8.                   (display-window wind))))
复制代码


并修改脚本 e 为:


  1. #!/bin/bash

  2. # first jump-or-exec to emacs
  3. cmd=`sawfish-client -e '(jump-or-exec-emacs)'`

  4. if [ "()" != "$cmd" ]
  5. then
  6.     # () stands for nil in lisp, if it is not (), then
  7.     # it should be the cmd returned by jump-or-exec-emacs
  8.     # to be invoked. See ~/.sawfishrc for more information
  9.     #
  10.     # $cmd
  11.     # simply $cmd won't work, because $cmd is "some cmd"
  12.     # with the quote mark, so I have to filter the quote
  13.     # mark first
  14.     cmd=${cmd%"}
  15.     cmd=${cmd#"}
  16.     $cmd
  17. fi

  18. # if there's argument then call emacsclient
  19. if [ $# -gt 0 ]
  20. then
  21.     emacsclient "$@"
  22. fi
复制代码


其中, jump-or-exec-emacs 里面的秒数可以根据自己  Emacs  的启动速度来设
定,这样是不是非常方便了?

=======================================================
    iswitch-window
=======================================================


毕竟不是所有的程序都是只启动一个实例就够了,有些程序需要启动多个实例,
用  切换起来就比较痛苦,这里介绍了在 Sawfish 里面模拟
fluxbox 的把几个窗口放到一个窗口的多个 tab 里面的方法。

这里要介绍的 iswitch-window 是一个方便切换窗口的扩展,就像  Emacs  的
iswitch-buffer 一类的扩展那样,列出窗口列表,并根据你的输入动态更新,过
滤掉不匹配输入的项,切换起来还是非常方便的。配置也非常方便,只需要在
这里下载 iswitch-window.jl ,放到 load-path 里面,然后在 ~/.sawfishrc
里加上这两行即可:


  1. (require 'iswitch-window)
  2. (bind-keys global-keymap "H-s" 'iswitch-window)
复制代码




=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  快捷键
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


=======================================================
    选择合适的快捷键
=======================================================


通常,常用的 ControlShiftAlt 相关的快捷键都被  Emacs  占据,不过
现在普通键盘上通常还有左右两个 Win 键,正好可以让我们用于窗口管理器的
操作。这里我把左边的 Win 键映射到 Super ,同时我也设定  为
Super ,然后把右边的 Win 键映射到 Hyper 。通常我用 Hyper 来进行切换窗
口相关的操作(如  和  等),而用 Super 来进行
窗口控制(如移动、改变大小、最大化、关闭等)。

映射 Win 键可以用 xmodmap 程序来完成,这里介绍了映射的方法。还可以用这
个方法来 SwapCapsCtrl 。这里是我的 ~/.Xmodmap 文件:


  1. keycode 115 = Super_L
  2. keycode 116 = Hyper_R
  3. clear mod3
  4. add mod3 = Super_L
  5. clear mod4
  6. add mod4 = Hyper_R
复制代码


配置好之后要保证启动 X 的时候 xmodmap 程序去加载这个文件,重新启动一下
X ,并在一个 XTerminal 里面输入 xmodmap ,看是否有我们写进去的这些内容,如
果没有的话,则要在 ~/.xinitrc~/.xsession 之类的文件里面手工加上一
句:


  1. xmodmap ~/.Xmodmap
复制代码



=======================================================
    如何表示快捷键
=======================================================


Sawfish 里面表示快捷键的方法和  Emacs  里面差不多,但是又不是完全一样,最
郁闷的就是想要把某个快捷键绑定到某个命令上,但是有不知道该如何写。这里
有一个函数可以查看输入的快捷键的名字,有点类似于  Emacs  里面的 C-h k
不过这个并不显示键的绑定信息,也不会连续抓取 key sequence ,但是还是很
有用的:


  1. (defun kid-show-key ()
  2.   (interactive)
  3.   (require 'keymap)
  4.   (kid-display-message (concat "You input ""
  5.                                (event-name (read-event "Please input the key: "))
  6.                                """)))
复制代码


显示出来的快捷键的名字就可以直接作为 bind-keys 的参数。


=======================================================
    使用 key sequence
=======================================================


Sawfish 也可以像  Emacs  那样使用 key sequence ,方法和  Emacs  差不多,都
是使用 keymap ,不过新版本的  Emacs  在 define-key 等函数中会自动判断并
定义需要的 keymap ,而在 Sawfish 中则需要手动定义 keymap 。这里我写了
一个 kid-bind-keys ,能够自动定义 keymap :


  1. (defun string-split (string regex #!optional (align 0) (n 0))
  2.   (if (string-match regex string n)
  3.       (append (list (substring string n (+ align (match-start))))
  4.               (string-split string regex align (match-end)))
  5.     (list (substring string n))))

  6. (define kid-keymap-alist ())

  7. (defun kid-bind-keys (keymap key action)
  8.   (do ((last-keymap keymap)
  9.        (keys (string-split key " ") (cdr keys)))
  10.       ((null keys))
  11.     (bind-keys last-keymap
  12.                (car keys)
  13.                (if (null (cdr keys))
  14.                    action
  15.                    (let ((current-keymap (assoc key kid-keymap-alist)))
  16.                      (setq last-keymap (or current-keymap
  17.                                            (cdar (setq kid-keymap-alist
  18.                                                        (cons (cons key (make-keymap))
  19.                                                              kid-keymap-alist))))))))))
复制代码


这样,就可以像  Emacs  那样直接设定复杂的 key sequence 了,例如:


  1. (kid-bind-keys global-keymap "W-h k" 'kid-show-key)
复制代码


但是要保持定义快捷键的方式一致,如果你一会自己手动定义 keymap ,一会儿
使用 kid-bind-keys 来定义,有可能会出问题,因为 kid-bind-keys 并不知道
你定义的 keymap 。另外,如果一个快捷键有多种表示方式(例如,在我这里
Super-hW-h 是同样的,因为我把  设定成了 Super 键),也不
要多种表示混用,否则会导致某一种表示方式定义的快捷键丢失。



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  抓图
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


为了方便抓图,我自己试着写了一个来完成这个工作:


  1. ;;;; screenshot utilities
  2. (define-structure kid.util.capture

  3.     (export kid-capture-default-filename
  4.             kid-capture-current-window
  5.             kid-capture-region
  6.             kid-capture-desktop)

  7.     (open rep
  8.           rep.system
  9.           rep.io.files
  10.           rep.regexp
  11.           rep.threads
  12.           sawfish.wm.commands
  13.           sawfish.wm.misc
  14.           sawfish.wm.util.prompt
  15.           sawfish.wm.events
  16.           sawfish.wm.windows
  17.           kid.util.message)

  18.   (defvar kid-capture-default-filename "/tmp/capture00.png")

  19.   (define (kid-capture-compute-next-filename last)
  20.     (string-match "([0-9]*)\\.[^.]+$" last)
  21.     (let* ((name (substring last 0 (match-start 0)))
  22.            (num (substring last (match-start 1) (match-end 1)))
  23.            (ext (substring last (match-end 1)))
  24.            (len (length num)))
  25.       (concat name
  26.               (if (= 0 len)
  27.                   "00"
  28.                   (format ()
  29.                           (concat "%0" (prin1-to-string len) "d")
  30.                           (1+ (string->number num))))
  31.               ext)))

  32.   (defmacro define-capture (func args)
  33.       `(define (,func file)
  34.          (setq kid-capture-default-filename (kid-capture-compute-next-filename file))
  35.          (system (concat "sawfish-capture " ,args " " file " &"))))
  36.   (define-capture kid-capture-desktop "-window root")
  37.   (define-capture kid-capture-region "")
  38.   (define-capture kid-capture-current-window
  39.       (concat "-window "
  40.               (let ((w (or (current-event-window)
  41.                            (input-focus))))
  42.                 (if w
  43.                     (prin1-to-string (window-id w))
  44.                     "root"))))

  45.   (define-command 'kid-capture-desktop kid-capture-desktop
  46.     #:spec '(list (prompt-for-file "Capture desktop to file: " ()  kid-capture-default-filename)))
  47.   (define-command 'kid-capture-region kid-capture-region
  48.     #:spec '(list (prompt-for-file "Capture region to file: " () kid-capture-default-filename)))
  49.   (define-command 'kid-capture-current-window kid-capture-current-window
  50.     #:spec '(list (prompt-for-file "Capture current window to file: " () kid-capture-default-filename))))
复制代码


要能够正常工作,还需要一个脚本 sawfish-capture


  1. #!/bin/zsh

  2. # use import to capture the screen and then use xloadimage
  3. # to display it
  4. import "$@" && xloadimage "${argv[$#]}"
复制代码


可以看见其实是使用  ImageMagick  包的 import 命令抓图,并用 xloadimage
显示出来,而那么多的代码其实主要是处理文件名方面,如果能够计算出几乎每
次都很让人满意的默认文件名的话,抓图就很惬意了!在输入文件名的那个窗口
里面,虽然编辑功能比不上  Emacs  的 minibuffer ,但是也能够进行文件名补
全了。

要使用这个功能,把他放到合适的路径下(介绍了应该把模块放到什么路径
下面),并让那个  ZShell  脚本可以运行(如果没有  ZShell  ,你也可以用自己喜
欢的 shell 写一个类似的,或者,如果不需要抓图之后预览一下的话,可以直
接在 Sawfish 的 lisp 代码里面运行 import 抓图就行了。) ,然后可以绑定
到相应的快捷键:


  1. (require 'sawfish.wm.util.prompt)        ; 这个是必须的
  2. (require 'kid.util.capture)
  3. (kid-bind-keys global-keymap "Print" 'kid-capture-current-window)
  4. (kid-bind-keys global-keymap "M-Print" 'kid-capture-current-desktop)
  5. (kid-bind-keys global-keymap "C-Print" 'kid-capture-region)
复制代码



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Sawfish 小技巧
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


=======================================================
    nil 的引用问题
=======================================================


注意在 Sawfish 配置里面, (null (car '(nil))) 返回为假,不过可以这样来
得到返回为真的值: (null (car `(,nil))) 。这是一个容易犯错误的地方,不
过在  Elisp  或者是  CommonLisp  中, nil'nil 是相等的,所以不会出现这
种情况。还有一个办法就是使用 () 来代替 nil(null (car '(()))) 求值就
会得到想要的 t

我想出现这样事情的原因是在 librep[1] 里面仅仅以 () 来表示假,而 nil
是一个普通的值为 ()symbol 而已,这个 symbol 本身并没有得到特殊对待
而理解为假。

=======================================================
    显示消息
=======================================================


Sawfish 提供了 display-message 函数可以在屏幕上显示一段文字,不过这段
文字并不会自动消失,于是我自己弄了一个函数,可以使用 display-message
来显示一段文字,并让消息在指定的一段时间之后自动消失:


  1. (require 'rep.io.timers)
  2. (defun kid-display-message (message &optional seconds)
  3.   "display MESSAGE for SECONDS seconds and make the message disappeared.
  4. The default display-message don't close the message window automatically"
  5.   (interactive)
  6.   (display-message message)
  7.   (make-timer
  8.    (lambda ()
  9.      (display-message))                        ; close message window
  10.    (or seconds 3)))
复制代码


=======================================================
    wm-modifier
=======================================================


在设定 Sawfish 的快捷键的时候经常看到 W-h 这类的快捷键, W 其实就是
wm-modifier ,这是可以定制的,不过通常我们设定他为 Super 键,这样可以
避免和  Emacs  的快捷键冲突。可以在 sawfish-ui 里面设定这个值。


=======================================================
    启动程序
=======================================================


我使用 startx 来启动 X ,所以我的必要的程序都在 ~/.xinitrc 里面启动,
但是在 Sawfish 退出的时候并不知道其他程序的存在,所以他们经常是异常退
出。一个办法就是在 Sawfish 里面使用 start-process 来启动程序,并在退出
之前结束掉它们。


  1. (define kid-startup-programs
  2.   '(("xloadimage" "-onroot" "-fullscreen" "/home/kid/.desktop")
  3.     ("xscreensaver" "-no-splash")
  4.     ("scim" "-d")
  5.     ("asclock" "-theme" "/home/kid/.asclock/kid")))
  6. (mapc (lambda (program)
  7.         (apply start-process (make-process standard-output) program))
  8.       kid-startup-programs)
  9. (add-hook 'before-exit-hook
  10.           (lambda ()
  11.             (mapc stop-process (active-processes))))
复制代码


=======================================================
    模块
=======================================================


Sawfish 的扩展可以写成一个一个的模块,通过 require 来加载,例如
sawfish.wm.util.prompt 。Sawfish 到 load-path 里面去找对应的模块,例
如,它会去 load-path 中的目录下面的 sawfish/wm/util 子目录下面寻找
prompt.jlprompt.jlc 文件来加载 sawfish.wm.util.prompt 模块。

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Sawfish 在线资源
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


- 王垠的关于 Sawfish 的介绍
- Sawfish 的主页
- Sawfish Wiki
- 一个 Sawfish 的教程
- Sawfish 在 GMANE 的邮件列表在线浏览
- 一些使用 sawfish 的人的站点上的资料
    - http://toykeeper.net/sawfish/
    - http://www.davep.org/sawfish/
    - http://yann.hodique.free.fr/index.php?pagename=Perso.Sawfish

==================== Footnote ====================
[1] librep 是 Sawfish 的脚本语言,一种很类似于  Elisp  的 Lisp 方言。
发表于 2006-10-9 01:47:04 | 显示全部楼层
蛮详尽的~
字数字数
回复 支持 反对

使用道具 举报

发表于 2006-10-9 12:15:13 | 显示全部楼层
Sawfish有什么特色?
回复 支持 反对

使用道具 举报

发表于 2006-10-9 13:09:14 | 显示全部楼层
不错不错,支持一下
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-10-9 15:57:50 | 显示全部楼层
Post by qobnvi
Sawfish有什么特色?
Sawfish is an extensible window manager using a Lisp-based scripting language --all window decorations are configurable and all user-interface policy is controlled through the extension language. This is no layer on top of twm, but a wholly new architecture.

Despite this extensibility its policy is very minimal compared to most window managers. Its aim is simply to manage windows in the most flexible and attractive manner possible. As such it does not implement desktop backgrounds, applications docks, or other things that may be achieved through separate applications.

All high-level wm functions are implemented in Lisp for future extensibility or redefinition. Currently this includes menus (using GTK+), interactive window moving and resizing, virtual workspaces, iconification, focus/transient window policies, frame theme definitions, and many more standard window-manager functions.

User-configuration is possible either by writing Lisp code in a personal .sawfishrc file, or through the integrated customization system (using GTK+, see the third and fourth screenshots below).

If you're wondering why there are many references to something called ``sawmill'' on this page, that's because sawfish was originally known by that name, but had to change.

我自己感觉的话,一个特点是 lisp 写成,并可以用 lisp 来定制。一个特点是支持 key sequence ,就像 Emacs 那样可以使用 C-x RET r 这类“很长”的快捷键。一个就是他是一个纯粹的窗口管理器,它的 FAQ 上这样写道:
Post by FAQ

# I installed Sawfish but it's not working! All I see when I start X is the default stipple background: no programs, no menus, no pager.

This is exactly what it's supposed to do. Sawfish is a window manager and as such is not responsible for setting the background, starting programs or displaying a pager--these can all be done using separate applications (e.g. by using a desktop environment such as GNOME).

The default menu binding is somewhat obscure; you must middle-click on the background to bring up the menus. (If you have a two-button mouse, try clicking both buttons simultaneously)

If, after reading this, you still think that sawfish isn't working, please send mail describing the problem to the sawfish mailing list sawmill@aarg.net
回复 支持 反对

使用道具 举报

发表于 2006-10-9 18:02:56 | 显示全部楼层
相较于fvwm及fluxbox,
它有什么出彩的地方?
回复 支持 反对

使用道具 举报

发表于 2006-10-9 18:16:40 | 显示全部楼层
可以使用 Lisp 的强大功能 :p
回复 支持 反对

使用道具 举报

发表于 2006-10-9 18:46:22 | 显示全部楼层
lisp强大的功能?
可否告知一二?
回复 支持 反对

使用道具 举报

发表于 2006-10-9 19:01:06 | 显示全部楼层
一些特性支持函数式编程、宏、继续等非常方便。Lisp 似乎在计算机和逻辑之间建立了一个屏障,在一定程度上可以使人避免陷入到计算机的一些概念的陷阱中去。呵呵,不过有些东西是说不清楚的,不经历过,没有亲身的体验是感觉不到的。
回复 支持 反对

使用道具 举报

发表于 2006-10-9 21:28:48 | 显示全部楼层
Post by herberteuler
Lisp 似乎在计算机和逻辑之间建立了一个屏障,在一定程度上可以使人避免陷入到计算机的一些概念的陷阱中去。

真的么!听起来相当诱人。
那sawfish的界面定制能力较fvwm如何呢(毕竟wm要看的)?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表