試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

X Window System上のビープ音を利用して通知音を出す

かなり前に実験的に作成したものだが、使い方によっては有用なので、Mail Notificationの記事とともに紹介。
libX11を使用する小さなC言語のプログラム*1で、「XChangeKeyboardControl()関数により音の高さなどを設定し、XBell()関数によりベルを鳴らす」という処理をする。
ベル自体は、Linuxカーネルの「pcspkr」モジュールが有効になっているか、組み込みになっていないと鳴らないので注意。*2これは、Linux 2.6.23の時点では

Device Drivers  --->
 Input device support  --->
  [*] Miscellaneous devices  --->
   <M> PC Speaker support           [CONFIG_INPUT_PCSPKR]

の場所にある。モジュールにすると、「modprobe -r pcspkr」でモジュールを削除することで鳴らなくすることもできて便利。

(2008/1/23)XOpenDisplay()関数にNULLを渡すことで関数変数DISPLAYを使用するように修正
ファイル名: notify-bell.c

/* Xのターミナルベルを使用した通知音
gcc -Wall -O2 notify-bell.c -o notify-bell -lX11
(古い環境では「-L/usr/X11R6/lib」も必要)
*/
#include <X11/Xlib.h>
#include <unistd.h>
#include <stdlib.h>

#define bell_set(dpy, ctl, volume, pitch, duration)                       \
{                                                                         \
    ctl.bell_percent = volume;                                            \
    ctl.bell_pitch = pitch;                                               \
    ctl.bell_duration = duration;                                         \
    XChangeKeyboardControl (dpy,                                          \
                            KBBellPercent | KBBellPitch | KBBellDuration, \
                            &ctl);                                        \
}
#define bell_restore(dpy, ctl, state)                                     \
{                                                                         \
    ctl.bell_percent = state.bell_percent;                                \
    ctl.bell_pitch = state.bell_pitch;                                    \
    ctl.bell_duration = state.bell_duration;                              \
    XChangeKeyboardControl (dpy,                                          \
                            KBBellPercent | KBBellPitch | KBBellDuration, \
                            &ctl);                                        \
}
#define bell(dpy, ctl, volume, pitch, duraton)                            \
{                                                                         \
    bell_set (dpy, ctl, volume, pitch, duraton);                          \
    XBell (dpy, 0);                                                       \
    XFlush (dpy);                                                         \
}

int
main (int argc, char **argv)
{
    XKeyboardState state;
    XKeyboardControl ctl;
    Display *dpy = XOpenDisplay (NULL);
    XGetKeyboardControl (dpy, &state); /* 実行前の状態を記憶 */

    /* 音を鳴らす */
    bell (dpy, ctl, 20, 1750, 150);
    usleep (111111);
    bell (dpy, ctl, 15, 1500, 200);

    /* 実行前の状態に戻す */
    bell_restore (dpy, ctl, state);
    XCloseDisplay (dpy);

    return EXIT_SUCCESS;
}

これをビルドして${HOME}/bin/などに入れて実行する。

$ gcc -Wall -O2 notify-bell.c -o notify-bell -lX11
$ install -s notify-bell ~/bin/
$ ~/bin/notify-bell

下のソースは、同じときに実験の中で作成した、ベルで時報っぽいものを鳴らすプログラム。実用性はあまりないかも...

ファイル名: timesig.c

/* Xのターミナルベルを使用した通知音
gcc -Wall -O2 timesig.c -o timesig -lX11
(古い環境では「-L/usr/X11R6/lib」も必要)
*/
#include <X11/Xlib.h>
#include <unistd.h>
#include <stdlib.h>

#define bell_set(dpy, ctl, volume, pitch, duration)                       \
{                                                                         \
    ctl.bell_percent = volume;                                            \
    ctl.bell_pitch = pitch;                                               \
    ctl.bell_duration = duration;                                         \
    XChangeKeyboardControl (dpy,                                          \
                            KBBellPercent | KBBellPitch | KBBellDuration, \
                            &ctl);                                        \
}
#define bell_restore(dpy, ctl, state)                                     \
{                                                                         \
    ctl.bell_percent = state.bell_percent;                                \
    ctl.bell_pitch = state.bell_pitch;                                    \
    ctl.bell_duration = state.bell_duration;                              \
    XChangeKeyboardControl (dpy,                                          \
                            KBBellPercent | KBBellPitch | KBBellDuration, \
                            &ctl);                                        \
}
#define bell(dpy, ctl, volume, pitch, duraton)                            \
{                                                                         \
    bell_set (dpy, ctl, volume, pitch, duraton);                          \
    XBell (dpy, 0);                                                       \
    XFlush (dpy);                                                         \
}

int
main (int argc, char **argv)
{
    int i;
    XKeyboardState state;
    XKeyboardControl ctl;
    Display *dpy = XOpenDisplay (NULL);
    XGetKeyboardControl (dpy, &state); /* 実行前の状態を記憶 */

    /* 準備音 x 3 */
    for (i = 0; i < 3; i++)
    {
        bell (dpy, ctl, 28, 666, 150);
        usleep (333333);
        bell (dpy, ctl, 25, 570, 150);
        usleep (666666);
    }

    /* 時報音 */
    bell (dpy, ctl, 30, 875, 800);

    /* 実行前の状態に戻す */
    bell_restore (dpy, ctl, state);
    XCloseDisplay (dpy);

    return EXIT_SUCCESS;
}

*1:プログラミングに詳しいわけではないので、書き方が変なところがあるかもしれない。値のハードコーディングが多い(主に「bell」マクロとusleep()関数)のもあまり良くはないと思うのだが...

*2:ディストリのカーネルではモジュールになっている可能性が非常に高く、カスタムカーネルでわざと外すことをしない限りは問題ない場合がほとんど