Categories

Linux 下 Thinkpad 多媒体键 OSD

thinkpadThinkpad 的键盘上有一些特殊地按键如音量控制等,在 Linux 下可以通过 tpb 这个工具利用起来,tpb 可以识别这些按键并执行一系列地操作,例如调用某个脚本或者用 xosd 在屏幕上显示(音量、亮度等)一个状态。不过我新装的这个 Arch Linux 的 tpb 在 Thinkpad X200 下工作不太正常,于是用了另一个办法来解决,使用很浮云地 notify-osd 加上自制的脚本来实现相同的目的。

以下是废话:
最近频繁地背着笔记本往来于 ZJG 和 YQ 两个校区,原来那个 Thinkpad R60e 不仅非常重,而且散热量非常大,看到毕业结算的时候学校退了一些学费,于是加上自己攒的一些钱买了一个 Thinkpad X200 ,顿时感觉好轻呀! 😀

X200 预装了 Windows Vista Home Basic 这个非常鸡肋的系统,一开始我想着好歹还是正版,还是留着吧,可是用了一下发现又卡又烦人,发现 Vista 竟然比我记忆中的要难用这么多啊,再想至少保留一个备份吧,可是用系统自带的工具折腾了好久都没有成功,一怒之下就全盘 erase 了装成 Linux 了。-.-bb

当时拿到 R60e 的时候是立即装了 Debian ,这次是装的 Arch Linux ,都是整个硬盘全部 erase ,所以隐藏分区也没有了,那个万能地 ThinkVantage 键之后拿来做什么还是慢慢想吧,Thinkpad 要是改回以前那样送光盘多好。

以下是正文:
装好系统之后配置声音的时候选择了 ALSA ,其实装好 alsa 相关的包然后运行 alsaconf 就有声音了,不过键盘上的音量控制按钮很奇怪,可以静音,但是却没法调整声音,似乎和之前的 R60e 不一样,这个不是 hardware mixer ,而是 software mixer ,在网上搜索了许多方法设置都不可以,tpb 的配置文件里面倒是有选项是选择可以使用 software mixer 的,但是依然没有效果,尝试各种配置之后得到的结果如果不是没有变化的话,就是更差了。所以最后我决定直接绑定按键用命令来调整音量。

不过整个声音系统似乎本身也比较奇怪,用 amixer 或者 alsamixer 设置 mute 之后音乐还是照常正常播放的,所以要 mute 似乎只有保存一下当前的音量,然后把音量设置为 0 ,在 unmute 的时候则读入之前保存的音量。不过,即使采取了这个办法,也还有一个问题:静音按钮无法捕获。

这真是非常奇怪的事,因为我之前确实捕获到这个按钮,并且在 xev 里查到了它的 keysym 是 XF86AudioMute ,但是后来却怎么都捕获不到了,不过 tpb 却是可以识别到,所以我确信那个按钮还没有坏。因为之前在尝试解决的时候用了各种配置,所以应该是某个配置导致的吧。经过各种尝试,才终于发现了原来要在系统启动的时候内核参数加上 acpi_osi="Linux" (就是加在 /boot/grub/menu.lst 文件的 kernel 那一行)参数才能识别出这个按键来。不过网上有说加上了这个参数之后调整音量就正常了,可惜我的 tpb 还是无法工作,并且(不确定是不是哪个步骤的配置又影响到了)其他部分也变得不太好用了,比如亮度调整的时候如果连续的按键的话,几次调整的状态会重叠显示在屏幕上。所以我决定彻底放弃 tpb 了。

其实自己绑定按键执行相应的命令也还是挺方便的。我用的窗口管理器是 Openbox ,首先运行 xev ,按下对应的键,从输出中找到对应的 keysym ,比如 XF86AudioRaiseVolume ,然后在 ~/.config/openbox.rc.xml 的 keyboard 一节加上这样的配置:

<keybind key="XF86AudioRaiseVolume">
    <action name="Execute">
        <execute>amixer set Master 2+</execute>
    </action>
</keybind>

然后重新加载一下配置文件就可以了。而静音的话,可以写一个脚本来先保存当前音量。不过,这样虽然可以调整音量,但是只是盲按,没有一点具体的反馈(比如什么时候已经达到最大音量了)还是不太方便。

但是要在屏幕上显示那样的东西,我还是只想到 xosd 或者 dzen2 ,dzen2 似乎比较难控制一点,关键是两次按键是会以两个独立的脚本运行起来的,而 xosd 似乎主要是提供了可编程的 API ,可执行文件就是 osd_cat ,可以在屏幕上显示一个文件的内容,不过其实也可以做,比如用一个比较大的字体显示

Volume:
|||||-----

其实也就相当直观了,tpb 画出来的图也就是差不多这个样子而已。只是这个方案很快就又被否决了,notify-osd问题还是出在重复按键上,如果我连续按几下的话,几次显示的东西就重叠在一起了,根本不具有可用性。就在我都决定要放弃实时显示,只在静音或者音量达到最大、最小时用 libnotify 发一个提示消息的时候,不小心找到了 notify-osd 这个东西。似乎是 Ubuntu 社区开发出来的东西,看起来相当浮云,在 Arch 仓库里暂时还没有,不过在 AUR 上又相应的包。用这个东西代替那个很土的 notification-daemon 就可以实现实时显示了。

notify-osd 使用的是 notification-daemon 同一个接口,所以也只要通过 notify-send 来发送提醒就可以了,它的弹出提示有淡入淡出效果并且是半透明的(当然,需要开启了 Composite 的支持),并且还有许多很先进的地方,比如显示一个状态条,正好用于音量的显示,并且它还能合并多次 notify ,也就是说我连续按下几次调整音量的话,它会直接更新原来的那个 notify bubble ,而不是弹出一个新的 bubble 。

好了,废话说完了,下面开始动工!首先是 notify-osd ,如果装好了 yaourt ,只需要运行

yaourt notify-osd

然后按照提示选择,就会自动下载并编译、安装好了,如果之前已经安装了 notification-daemon ,则先卸载之。重新启动一下 X 即可生效,不过,Openbox 没有 composite 的支持,notify-osd 不能半透明,这个可以使用 xcompmgr 来辅助,不过我不需要阴影和淡入淡出,所以直接运行 xcompmgr ,把下面这一行加入到 ~/.config/openbox/autostart.sh 中即可:

xcompmgr &

这里顺便提一个问题,我一开始运行 xcompmgr 的时候屏幕疯狂地输出类似如下的错误:

error 168 request 153 minor 8 serial 1919
error 168 request 153 minor 8 serial 2131
error 168 request 153 minor 8 serial 2239
error 168 request 153 minor 8 serial 2347
error 168 request 153 minor 8 serial 2455
error 168 request 153 minor 8 serial 2563

查了一下原来是应为我使用 xloadimage 来设置桌面壁纸的缘故,它的某个不规范的行为(没有将 _XSETROOT_ID 设置为一个有效的 pixmap )让 xcompmgr 郁闷了,改用 feh 来设置壁纸之后就 OK 了。事实上我在很久很久前 Debian 中就遇到了这个问题,只是当时怎么都没有想到会是桌面壁纸的问题。 -.-bb

然后是控制音量并显示提示的脚本,控制音量使用 amixer ,静音按照之前的方法将音量保存在 ~/.thinkpad-volume 文件中。另外,我还彻底抛弃了 tpb ,把亮度调节也加入到脚本中来,只是和音量调节不一样的是,亮度调节按下 Fn+HomeFn+End 就可以实现了,脚本里只要读出调节后的值再用 notify-osd 显示出来就可以了。脚本如下:

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
96
97
98
99
#!/usr/bin/python
# processing of thinkpad multi-media key
 
import sys
import re
import os
 
if len(sys.argv) < 2:
    print "Usage:"
    print "    %s cmd" % sys.argv[0]
    print ""
    print "  Available commands:"
    print "    - vol-up"
    print "    - vol-down"
    print "    - mute"
    print "    - brightness"
    sys.exit(1)
cmd = sys.argv[1]
 
########################################
# Configurations
########################################
MIXER   = "Master"
VOLSTEP = 2
VOLTMP  = os.path.expanduser('~/.thinkpad-volume')
BRFILE  = '/proc/acpi/video/VID/LCD0/brightness'
 
########################################
# Utility routines
########################################
def get_volume():
    info  = os.popen('amixer get %s' % MIXER).read()
    limit = re.search(r'Limits: Playback 0 - (\d+)', info).group(1)
    vol   = re.search(r'Front Left: Playback (\d+)', info).group(1)
    return (int(vol), int(limit))
 
def notify_percentage(subject, value, limit):
    percentage = value * 100 / limit
    if percentage < 30:
        icon = 'low'
    elif percentage > 70:
        icon = 'high'
    else:
        icon = 'medium'
    os.system('notify-send "%s" ' % subject +
              '-i notification-%s-%s ' % (subject, icon) +
              '-h int:value:%d ' % percentage +
              '-h string:x-canonical-private-synchronous:')
 
def adjust_volume(adjustment):
    curr_volume, limit = get_volume()
    dest_volume = curr_volume+adjustment
    if dest_volume < 0:
        dest_volume = 0
    if dest_volume > limit:
        dest_volume = limit
    os.system("amixer set %s %d unmute" % (MIXER, dest_volume))
    notify_percentage('audio-volume', dest_volume, limit)
 
def toggle_mute():
    curr_volume, limit = get_volume()
    if curr_volume == 0:
        try:
            orig_volume = int(open(VOLTMP).read())
        except:
            orig_volume = limit/2
        os.system("amixer set %s %d unmute" % (MIXER, orig_volume))
        notify_percentage('audio-volume', orig_volume, limit)
    else:
        open(VOLTMP, 'w').write('%d' % curr_volume)
        os.system("amixer set %s 0 unmute" % MIXER)
        os.system('notify-send "Volume" ' +
                  '-i notification-audio-volume-muted ' +
                  '-h int:value:0 ' + 
                  '-h string:x-canonical-private-synchronous:')
 
def get_brightness():
    info = open(BRFILE).readlines()
    limit = int(info[0].split()[-1])
    curr  = int(info[1].split()[-1])
    return (curr, limit)
 
def adjust_brightness():
    # brightness is adjusted from hardware, we just show
    # notification
    curr, limit = get_brightness()
    notify_percentage('display-brightness', curr, limit)
 
########################################
# Main routine
########################################
if cmd == 'vol-up':
    adjust_volume(VOLSTEP)   
elif cmd == 'vol-down':
    adjust_volume(-VOLSTEP)
elif cmd == 'mute':
    toggle_mute()
elif cmd == 'brightness':
    adjust_brightness()

然后在 Openbox 中把各个按键绑定到这个脚本上:

<!-- multimedia keys -->
<keybind key="XF86AudioMute">
    <action name="Execute">
        <execute>/home/pluskid/bin/thinkpad-mmkey.py mute</execute>
    </action>
</keybind>
<keybind key="XF86AudioRaiseVolume">
    <action name="Execute">
        <execute>/home/pluskid/bin/thinkpad-mmkey.py vol-up</execute>
    </action>
</keybind>
<keybind key="XF86AudioLowerVolume">
    <action name="Execute">
        <execute>/home/pluskid/bin/thinkpad-mmkey.py vol-down</execute>
    </action>
</keybind>
<keybind key="XF86MonBrightnessUp">
    <action name="Execute">
        <execute>/home/pluskid/bin/thinkpad-mmkey.py brightness</execute>
    </action>
</keybind>
<keybind key="XF86MonBrightnessDown">
    <action name="Execute">
        <execute>/home/pluskid/bin/thinkpad-mmkey.py brightness</execute>
    </action>
</keybind>

这下就可以有很浮云的实时提醒了! 😀

最后再说几句废话,X200 还是很不错的,不过 ESC 键那里的壳似乎有一点松,所以按的时候会啪啪地响,莫非是交给联想之后做工变得粗糙了? -,-bb 装 Linux 除了音量控制搞得有点崩溃之外,基本没有遇到问题,我还是比较喜欢 R60e 那样的 hardware mixer 。无线应该也没有问题,只是(不知道是不是考试的原因)最近一两周紫金港的无线网络完全瘫痪了,没法测试。宽屏分辨率也完全没有问题,装好 xf86-video-intel 之后甚至不需要生成 xorg.conf 就可以很正常地跑起 X 来(不过我还是启动不起 compiz ,一运行 fusion-icon ,X 就退出了,没有任何错误信息)。小红点支持也很好,甚至比 Windows 下还好用,Windows 下如果启用了小红点的滚屏功能的话,中键的很多功能都不能用了,比如在 Firefox 里用来在新标签中打开链接、关闭标签页等,然而在 Linux 下却可以两者兼得,不得不说小红点是我最喜欢的 Thinkpad 的设计。最后就是休眠了,似乎这些方面 Linux 还得持续改进才行,我一看到网上超级复杂的文档就开始退缩了。总的来说还是相当不错的!下面 show 一张截屏吧,Openbox + conky + tint2 :

openbox-small

23 comments to Linux 下 Thinkpad 多媒体键 OSD

  • galilette

    先推荐一个小黑的网站: http://www.thinkwiki.org

    说几点你可能会乎略的问题:
    0) 相关内核模块是CONFIG_THINKPAD_ACPI, 不过arch好像默认m了所有模块所以不是问题. 另外还有一个包叫thinkpad smapi (tpctl.sf.net)

    1) 电池充电的上下限, 这个对延长电池寿命很有用. 有0)之后, 相关文件在/sys/devices/platform/smapi/BAT0/start(stop)_charge_thresh. 以battery搜索thinkwiki有更详细的帖子

    2) 硬盘门事件..
    http://www.thinkwiki.org/wiki/Problem_with_hard_drive_clicking

    3) 电源管理. 可以考虑用laptop-mode-tools包.

    另外挂起/休眠我用的是tuxonice的kernel, 配套的用户工具是hibernate-script

    Fn热键绑定我用的是acpid (e.g., Fn-F4 suspend to ram, Fn-F12 suspend to disk, etc). 在某些型号下可能需要
    cat /sys/devices/platform/thinkpad_acpi/hotkey_all_mask > /sys/devices/platform/thinkpad_acpi/hotkey_mask
    来unmask所有Fn组合 (软件作者不推荐:D)

    更广义的热键绑定我用actkbd, e.g., 长按mute键mute, 短按则切换mute/unmute, etc. 我因为要用到tablet面板上的按键, 所以普通的肩膀定功能都单薄了点. 以前写的一个帖子(要翻墙):
    http://zeekish.wordpress.com/2009/03/26/actkbd/

    先打这些, 吃早饭去了

  • 果然Linuxer都是相当折腾呢!

    自从内核的一个版本,我这里Fn+x调整屏幕亮度就不工作了,不过似乎我平时不需要调亮度,音量似乎也没有特别频繁地要用快捷键调整的需要 -.-b 虽然 xev 都可以捕获到这些键,但是懒得设置执行程序了。

    notify-osd看起来很pp,考完试就换成它。顺便我也来折腾下这些快捷键…

    那张壁纸我以前也用过,很喜欢 :p

  • imonyse

    x200的屏幕实在太小,不喜欢。
    我用的是x61,但是那个右掌托发热的问题实在没法解决(我把BIOS里能关的选项都关了,系统设置上,又是延长系统写入脏页时间,又是调整电源管理策略…最终发觉温度还是比windows下要高3-4度),如果还要一直用无线,会非常的痛苦。编译一个emacs,CPU温度可以上95-98度… 如果要跑kvm,qemu,bochs或者xen(vmware要好一点),那简直…太心疼我的笔记本了。

    虽然居无定所,用笔记本搬家方便,但是我已经决定了:下一台电脑,一定买台式的…

  • @galilette
    多谢这些建议呢!用了电池设置和电源管理的东西,不过休眠还是不行,用 TuxOnIce 的 kernel ,由于一开始把 swap 分区只分了 256M ,又不想调整分区,就用休眠到文件的办法,也不知道是 ext4 文件系统的问题还是磁盘驱动的问题,不能成功从休眠中恢复。于是以后再考虑这个问题了,其实要用到休眠的时候好像也不多。

    ps: 那个是你的 blog 啊? 🙂

  • galilette

    关于休眠碰到的问题, 具体可能要看一下log文件了, 我这边是 /var/log/hibernate.log. 我能想到的几种可能:

    1) 在设置SuspendDevice时你可能用了file:/dev/sda:0x0000, 你可以把file改成swap试试 (但后面还是跟文件而不是swap分区)。 我最早装的时候用file会corrupt the swap file, 可能是tuxonice的bug,不知道现在修复没有

    2) 可能是因为initrd的问题,arch好像只是把ext4编译成模块的样子,tuxonice未必能及时载入文件系统模块。这个可能需要看一下tuxonice里关于initrd的文档。我在gentoo上是把这种启动相关的模块都编译进内核的,省掉一些麻烦

    挂起到硬盘还是蛮方便的, 有点像一个universal session manager

    那个blog我就是记录一点机器设置的问题,年纪大了全记脑子里记不住了

  • @galilette
    /var/log/hibernate.log 倒是挺正常的,不过 dmesg 有些错误,像这个样子:

    Open file /dev/sda3 returned (null) and name_to_devt failed
    

    我想要么就是文件系统要么就是磁盘驱动的问题吧,后来我改成 swap 分区了(只是先用小的分区试一下),结果是另一个错误:

    Can't translate "/dev/sda3" into a device id yet
    

    但是感觉是差不多的,而且这次是直接连 suspend 都不成功的。

    不过 modprobe scsi 是失败的,这个感觉应该已经被直接编译进内核了,而不是以模块的形式存在了吧?真奇怪。

  • galilette

    有没有做过这一步?
    http://wiki.archlinux.org/index.php/Tuxonice#Recreating_the_initramfs

    还有就是你第二个错误里为啥是hda不是sda呢?

    我给我爸的台机装了arch, 其他都不错, yaourt很好很强大, 就是感觉内核弄起来不及自己编译来得直接

  • @galilette
    应该是 sda ,在 dmesg 里的,我没有保存日志,就看了昨天的浏览器搜索记录,我搜索的时候去掉了具体的 partition ,所以刚才打错了。

    那一步确实做过了,我还尝试把 resume 这个 hook 放到 filesystem 后面也不行,按理说如果连 filesystem 都已经起来了,磁盘和文件系统应该都已经初始化好了啊。

    确实 yaourt 装内核的时候都不知道哪里可以有机会修改内核配置。

  • @pluskid
    恩,应该是修改 PKGBUILD 文件里把 make menuconfig 一行注释掉就可以配置内核编译选项了。 🙂

  • galilette

    http://aur.archlinux.org/packages.php?ID=15224
    第一条bug不知道跟你的有没有关系

  • galilette

    btw gentoo上tuxonice还停留在2.6.29, 所以你说的问题也可能是patchset和.30不完全匹配有关. tuxonice官方还没有.30的稳定版发布

  • @galilette
    也许是呢,没有验证过,不过想起来 yaourt 下载的其中一个 patch 的 md5sum 和 PKGBUILD 中列的不一样,我当时忽略掉了这个问题,应为 patch 倒是能正常打上的。现在这个样子,好像 Arch 里 2.6.30 的 kernel 也才刚启用不久,还是等一段时间再来看看好了。

  • zhouyuan

    米男阿!

  • mifly

    arch下小红点还不错,默认的灵敏度比xp的高点,xp下我调了下,跟arch差不多了。中键也不错,但不能左右滚动,现在已经习惯用ctrl + w关闭标签,ctrl + 左键 在新标签打开链接了。
    arch下的风扇呼呼作响。x200的字体好小,xp下我把dpi调到了110来用,但某些程序的界面变形了,而arch下dpi可以调到250,字体和界面都自适应变得,这个好。据说vista下dpi和linux的类似,xp的不行。

  • transtone

    @pluskid
    如果不考虑休眠时的界面,个人觉得tuxonice已经非常鸡肋了。现在不管是ibm T43还是hp nc6400中我都是用pm-suspend/pm-hibernate来待机与休眠,无需其他设置,非常的方便。pm-utils应该算是系统的核心包了,arch应该也是带了的吧(俺用gentoo)。

  • @transtone
    Arch 确实也有那个包,不过我的 swap 分区分得太小了,需要能支持 suspend to file 的支持才行。

  • Mike

    昨天刚入手x200。。。。

    就差这个浮云了。。。嘿嘿

  • Mike

    然后发现只要启动gnome-settings-daemon就好了。。。-,-

    看来ubuntu下了很多功夫啊。。。。

  • @Mike
    恩,gnome-daemon 会处理音量调节,但是亮度好像没有。而且我现在好像如果没有先锁定就把盖子合上,然后过一段时间再打开的话就一直黑屏,得强行重启或者远程重启,不知道是不是它自动休眠却休眠失败了,真是……

  • Mike

    @pluskid
    应该是gnome-power-manager管亮度的。最开始我没启动gnome-settings-daemon的时候就没有声音,启动了它,有屏幕亮度。。。。

    我的x200是7457a46,suspend和hibernate都没有任何问题啊。。。难道是ubuntu用了一些神奇的驱动?@_@

  • @Mike
    不一定是驱动吧,估计是打过 kernel patch 什么的或者有一些额外的配置文件,只是也许自己弄的话搞起来毕竟麻烦,但是如果用 Gnome 的话,它应该自动搞定了一些东西了,如果用 Ubuntu 下的 Gnome 的话,应该有更多的东西被自动搞定了,像休眠什么的。

  • @Mike
    不管怎么说,我不太喜欢它这种 soft mixer ,感觉还是那种硬件控制声音的好用一些。

  • Nino

    osd_cat 可以直接显示“|||—“,用–barmode参数
    试试看:
    我的OSD_Volumn.sh:

    #!/bin/sh

    # get volumn value from system
    volumn_percent=$(amixer get Master | grep –color=never Mono: | sed -e s/” Mono: Playback [0-9]* \[“// -e s/”%.*”//)

    # show it by percentage
    killall osd_cat
    osd_cat –pos=bottom –align=center –color=green –shadow=3 –delay=1 –barmode=percentage –percentage=$volumn_percent &

    然后加到.xbindkeys中就可以了:
    “amixer sset Master 1-;~/.myscript/OSD_Volumn.sh”
    m:0x10 + c:122
    Mod2 + XF86AudioLowerVolume