我还是Thinkpad X32的时候,一直是用tpb来关联笔记本的acpi事件。省心是省心,不过弄得我一直觉得acpi好神秘的。现在换了Asus UL30A,发现在Arch下音量调节的快捷键都不能用了,没办法,只好自己动手。

起了个acpid,到/etc/acpi下瞟了眼,两个目录eventsactions,一个文件handler.sh。又翻了下里面的文件,大概明白了。

  • events目录里面是你要捕捉的acpi事件,文件格式如下(下面的是我用来定义华硕笔记本快捷键的)

    要捕捉的事件,正则匹配

    event=hotkey / .*

    执行的脚本,用%e将事件的名字传递

    action=/home/roylez/lib/acpi/asus_button.sh %e

  • actions是默认放执行的脚本的目录,但是你不放这里也没什么影响。比如我就把脚本放自己的home了。

  • handler.sh会捕捉其他没有被捕捉的事件,一般不需要改。

于是,我先用acpi_listen捕捉了静音、音量减小和音量加大的acpi事件,记下来,分别是hotkey ATKD 00000032 ...hotkey ATKD 00000031 ...hotkey ATKD 00000030 ...

然后,我写了个叫做asus_button.sh的脚本

#!/bin/bash
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 定义Asus UL30A acpi事件
script_dir=/home/roylez/bin
export DISPLAY=:0
set $*

# "hotkey ATKD 00000030 ...."
case $3 in
    00000032)   # mute
        su roylez -c "$script_dir/change_volume toggle"
        ;;
    00000031)   # volume down
        su roylez -c "$script_dir/change_volume down"
        ;;
    00000030)   # volume up
        su roylez -c "$script_dir/change_volume up"
        ;;
    0000005c)   # suspend-hybrid
        pm-suspend-hybrid &
        ;;
esac

再然后,我在我的bin目录里面放了个用来改变音量的脚本,就叫做change_volume

#!/usr/bin/env ruby
# coding: utf-8
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 更改音量并用notify-send做出通知

$: << File.join(ENV['HOME'], 'lib/ruby')

require "msg.rb"

class VolumeControl
    def initialize
        @step = 5
        @notify = File.file?("/usr/bin/notify-send") ? :show_notification : nil
    end

    def mute_toggle
        set "toggle"
    end

    def set(control)
        `amixer sset Master #{control}`
        show
    end

    def down
        set("#{@step}%- unmute")
        `aplay #{ENV['HOME']}/.sounds/MACSound/Open.wav`
    end

    def up
        set("#{@step}%+ unmute")
        `aplay #{ENV['HOME']}/.sounds/MACSound/Open.wav`
    end

    def show
        @status = `amixer get Master`
        puts @status
        @notify.nil? || send(@notify)
    end

    private

    def show_notification
        volume = mute = nil
        if @status =~ /Playback.*?\[(.*?)%\].*?\[(on|off)\]/m
            volume = $1.to_i
            mute = $2
        end if volume && mute
            status = case volume
                     when 0..33     then    'low'
                     when 34..66    then    'medium'
                     when 67..100   then    'high'
                     end
            status = 'muted' if mute == 'off'
            icon = "/usr/share/icons/Tango/scalable/status/audio-volume-#{status}.svg"
            title = mute == 'off' ? "\n    扬声器已静音" : "\n    现在音量是 #{volume}%"
            msg :icon => icon, :text => '' , :title => title
        end
    end
end

if __FILE__ == $0
    require 'optparse'

    vc = VolumeControl.new

    case ARGV.first
    when 'help'
        puts "#{$0} <command>"
        puts "    help      show this help"
        puts "    show      display current volume info"
        puts "    up        increase volume"
        puts "    down      decrease volume"
        puts "    toggle    toggle mute"
    when "show"
        vc.show
    when "up"
        vc.up
    when "down"
        vc.down
    when "toggle"
        vc.mute_toggle
    end
end

里面用到了Tango的图标主题,以及苹果系统的声音主题(有兴趣的给我发信),然后就是一个叫做msg.rb的脚本

#!/usr/bin/env ruby
# coding: utf-8
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 通用提醒脚本,随机选择仆人

icon_path = File.join( ENV['HOME'], '.icons')
servants = Dir.glob(File.join(icon_path, 'servants', '*.png'))
$icon = servants[rand(servants.length)]
$title = '主人,提醒您一下:'

case ARGV.length
when 1
    $text = ARGV[0]
when 2
    $title, $text = ARGV
when 3
    $icon = File.exist?(ARGV[0]) ? ARGV[0] : \
        File.exist?(File.join( icon_path , ARGV[0])) ? File.join(icon_path,ARGV[0]) : icon
    $title, $text = ARGV[1..-1]
else
    $title = '错误'
    $text  = '参数无效'
end

def msg(kwds={})
    kwds = { icon:$icon, title:$title, text:$text }.merge(kwds)
    kwds[:text] = %Q{<span size="12000" weight="bold">\n#{kwds[:text]}</span>}
    system(%Q{DISPLAY=:0.0 notify-send -i #{kwds[:icon]} '#{kwds[:title]}' '#{kwds[:text]}'})
end

if __FILE__==$0
    msg
end

最后的效果是这样的

volume_change

最后说说感想

  • 最难的地方是su roylez -c。一直没有想到acpi脚本是用root权限执行这个问题,导致notify-send的通知怎么也不能显示,su成我的用户名就好了。
  • 把华硕自带的电源管理键绑成了休眠,比Fn Fx什么的方便多了。合上屏幕就是关闭屏幕,按电源管理就是休眠,按电源就是关机。
  • pm-suspend-hybrid还是个半成品,目前等于suspend to memory。
  • change-volume脚本可以很方便的改用OSD,加一句判断什么的就成,不过我懒得动了。
  • tpb好难看。