第4章 イベントを聞く


第2章で窓を開くときに、窓が本当に開いたかを確認するため、Xserverから 送られてくるExposeイベントを取り込んでいました。イベントにはこの他にも たくさんの種類があり、端末で起こるさまざまな状況の変化がイベントとして Xclientに伝えられます。本章ではこのさまざまなイベントのうち、マウスの 状況の変化に関するイベントを受信してマウスで操作できるプログラムの 作り方を解説します。


マウスに関するイベント

マウスの状況の変化で発生するイベントは次の3種類です。

ButtonPress
マウスのボタンのどれかが押されると発生するイベントです。
ButtonRelease
マウスのボタンのどれかが離される発生するイベントです。
MotionNotify
マウスが動くと発生するイベントです。
これらイベントにはより詳しい情報が付随してきます。


イベントキュー

Xserverが送信するイベントはイベントキューと呼ばれるバッファに順次格納されます。 格納された順にXclientがそれを受け取っていくことになるのですが、受け取ってくれる Xclientがないと、そのイベントは自然消滅します。


イベントマスク

Xserverは実にさまざまなイベントを発生しますが、Xclientが必要としている イベントはそのうちわずかです。Xclientは必要とするイベントの種類を あらかじめ XSelectInput()関数で明示することにより、それらのイベントを 受けとることができます。明示されていないイベントは無視されます。

Prototype

XSelectInput( Display* display, Window window, long event_mask );

event_maskにその窓が受けとるイベントの種類を指定します。 マウス関連でここで指定することができるものは以下の7種類があります。

ButtonPressMask
ButtonPressイベントを受けとります。
ButtonReleaseMask
ButtonReleaseイベントを受けとります。
PointerMotionMask
MotionNotifyイベントを受けとります。
ButtonMotionMask
マウスのどれかのボタンが押されている時にのみMotionNotifyイベントを受けとります。つまりドラッグの検知です。
Button1MotionMask
マウスの左ボタンが押されている時にのみMotionNotifyイベントを受けとります。
Button2MotionMask
マウスの中ボタンが押されている時にのみMotionNotifyイベントを受けとります。
Button3MotionMask
マウスの右ボタンが押されている時にのみMotionNotifyイベントを受けとります。
上記の値のことをイベントマスクと呼びます。 複数の種類のイベントを受けとるには、これらの論理和 | (縦棒)でつなげて event_maskに指定します。


イベントを受けとる

イベントキューにはその窓に対して、Xserverから送られてくるイベントのうち XSelectInput() で指定した種類のイベントが貯められます。そのイベントをプログラム(Xclient)が受けとるには そのイベントを種々の関数を用いてキューから取り込まなければなりません。 取り込まれたイベントはXEvent型の構造体に格納されます。

イベントを取り込むための関数の一部を紹介します。

Prototype

void XNextEvent( Display* display, XEvent* event );
Bool XCheckMaskEvent( Display* display, long event_mask, XEvent* event );

XNextEvent()関数はイベントキューになにかイベントが入るまで待ちます。 イベントがキューに入るとそれをキューから取り出して、引数で指定した アドレスにあるXEvent型構造体に格納します。

XCheckMaskEvent()関数は指定のイベントマスクの種のイベントが キューに入っていなければ待たずに Falseを返します。あればそのイベントを キューから取り出して、引数で指定したアドレスにあるXEvent型構造体に 格納します。

両方の関数とも、この関数を呼ぶとXFlush()関数も自動的に 呼び出されます。そのため、この関数を連発して呼ぶとマシンへの 負荷が激しく重くなるので、連発しないように工夫してください。


イベント構造体 XEvent

XEvent型のイベント構造体にはイベントの種類の応じてさまざまな種類の情報が格納 されます。ここではマウスに関連するイベントのみについてその内容を紹介します。

例えばevというXEvent型構造体にイベントを取り込んだとしましょう。
ev.typeには取り込んだイベントの種類が記録されています。 例えばButtonPress,ButtonRelease,MotionNotifyなどの値です。

ev.type が ButtonPress または ButtonReleaseの場合には次の変数が利用可能です。

ev.xbutton.x
ボタンが押された/離されたところのX座標です。
ev.xbutton.y
ボタンが押された/離されたところのY座標です。
ev.xbutton.button
押された/離されたボタンの番号が入っています。左ボタンが1、中ボタンが2、右ボタンが3です。

ev.type が MotionNotifyの場合には次の変数が利用可能です。

ev.xmotion.x
ボタンが押された/離されたところのX座標です。
ev.xmotion.y
ボタンが押された/離されたところのY座標です。
ev.xmotion.state
ボタンの情報が入っています。
ev.xmotion.stateの値をButton1Mask,Button2Mask,Button3Mask のそれぞれと 論理積をとることによりどのボタンが押されているかを判別することができます。


マウスでお絵書きの例

マウスのイベントを利用して、ボタンを押すたびに直線を描くプログラムを示します。 マウス操作の理解を深めて下さい。

名前はmtest.ccです。是非おためしください。

mtest.ccのダウンロード

mtest.ccの内容

/* mtest.cc */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>

unsigned long GetColor( Display* dis, char* color_name )
{
    Colormap cmap;
    XColor near_color, true_color;

    cmap = DefaultColormap( dis, 0 );
    XAllocNamedColor( dis, cmap, color_name, &near_color, &true_color );
    return( near_color.pixel );
}


int main( void )
{
    Display* dis;
    Window win;
    XSetWindowAttributes att;
    GC gc;
    XEvent ev;

    dis = XOpenDisplay( NULL );
    win = XCreateSimpleWindow( dis, RootWindow(dis,0), 100, 100,
      256, 256, 5, WhitePixel(dis,0), BlackPixel(dis,0) );

    att.backing_store = WhenMapped;
    XChangeWindowAttributes( dis, win, CWBackingStore, &att );

    XSelectInput( dis, win, ExposureMask|ButtonPressMask );
    XMapWindow( dis, win );

    do{
        XNextEvent( dis, &ev);
    }while( ev.type != Expose );

    gc = XCreateGC( dis, DefaultRootWindow(dis), 0, 0 );
    XSetForeground( dis, gc, GetColor( dis, "yellow")  );

    int ox=0,oy=0;

    while(1){
        XNextEvent( dis, &ev );
        if( ev.type != ButtonPress ) continue;

        if( ev.xbutton.button == 1 ){
            XDrawLine( dis, win, gc, ox, oy, ev.xbutton.x, ev.xbutton.y );
            ox = ev.xbutton.x;
            oy = ev.xbutton.y;
            XFlush( dis );
            continue;
        }
        if( ev.xbutton.button == 3 ){
            break;
        }
    }

    XDestroyWindow( dis , win );
    XCloseDisplay( dis );

    return(0);
}

これで左ボタンで次々に線が引けます。右ボタンでプログラム終了です。


  • 第5章 色を織りなす
  • 目次
    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp