Xlibとか

ここのページを参考に、もう少しXlibを見てみた。keysymを受け取って、それで線を引いたり消したりすることを考えて、次のようにコードを足してみた。

  while(1) {
    XNextEvent(disp, &evt);
    :
    :
    if(evt.type==KeyPress&&
       XLookupString(&evt.xkey,text,255,&key,0)==1) {
      if(text[0]=='q'){
	break;
      }
      if (evt.type==ButtonPress) {
	printf("You pressed a button at (%i,%i)\n",
	       evt.xbutton.x,evt.xbutton.y);
      }
    }
    paint(cs);
  }

qキーを叩くとウィンドウが閉じるようになって、ちゃんと入力イベントをハンドルできてることが確認できた。でも、マウスボタンのイベントは受け取れていないっぽい。そもそもevt.xbutton.xって、どんなもんだと思って、そっちのほうを見てみた。evtはXEvent型で、これはXlib.hで共用体として定義されている。

typedef union _XEvent {
  int type;               /* must not be changed; first element */
  XAnyEvent xany;
  XKeyEvent xkey;
  XButtonEvent xbutton;
  XMotionEvent xmotion;
  XCrossingEvent xcrossing;
  XFocusChangeEvent xfocus;
  XExposeEvent xexpose;
  XGraphicsExposeEvent xgraphicsexpose;
  XNoExposeEvent xnoexpose;
  XVisibilityEvent xvisibility;
  XCreateWindowEvent xcreatewindow;
  XDestroyWindowEvent xdestroywindow;
  XUnmapEvent xunmap;
  XMapEvent xmap;
  XMapRequestEvent xmaprequest;
  XReparentEvent xreparent;
  XConfigureEvent xconfigure;
  XGravityEvent xgravity;
  XResizeRequestEvent xresizerequest;
  XConfigureRequestEvent xconfigurerequest;
  XCirculateEvent xcirculate;
  XCirculateRequestEvent xcirculaterequest;
  XPropertyEvent xproperty;
  XSelectionClearEvent xselectionclear;
  XSelectionRequestEvent xselectionrequest;
  XSelectionEvent xselection;
  XColormapEvent xcolormap;
  XClientMessageEvent xclient;
  XMappingEvent xmapping;
  XErrorEvent xerror;
  XKeymapEvent xkeymap;
  long pad[24];
} XEvent;

鬼のような羅列で、ちょっとビビった。こんなに共用体に何でもかんでも突っ込んでいいのか、なるほど。先頭にあるintは、それ以降で宣言されている各種イベントの型の先頭にも当然含まれている。

typedef struct {
  int type;               /* of event */
  unsigned long serial;   /* # of last request processed by server */
  Bool send_event;        /* true if this came from a SendEvent request */
  Display *display;       /* Display the event was read from */
  Window window;          /* "event" window it is reported relative to */
  Window root;            /* root window that the event occurred on */
  Window subwindow;       /* child window */
  Time time;              /* milliseconds */
  int x, y;               /* pointer x, y coordinates in event window */
  int x_root, y_root;     /* coordinates relative to root */
  unsigned int state;     /* key or button mask */
  unsigned int button;    /* detail */
  Bool same_screen;       /* same screen flag */
} XButtonEvent;
typedef XButtonEvent XButtonPressedEvent;
typedef XButtonEvent XButtonReleasedEvent;

なるほど。共用体ってこうやって使うのか。それにしてもXEventの最後にlong pad[24]とあるのって大丈夫なのか。イベントの種類によって構造体のサイズが違うからパディングしてるってことなんだろうけど、もしうっかりあふれたりしたら、あいや、あふれるとかあるわけないか。ていうか、なんでパディングしてるんだろうか。

Xlibを見ていたら、ちょっとしたグラフィックで遊ぶぐらいなら、何もCairoなんて使わなくてもいいということに気がついた。JPEGを使ったり、アルファブレンディングしたいとか、そういう高度なグラフィックでもないかぎり、Xlibでも充分かも。

いくつか気になった。

Bool型ってCだと定義されてなくて、実際、Xlib.hに

#define Bool int

と書いてある。これはどうも一般的なやり方らしいし、ほかにやりようがないのだろうけど、Booleanがintでいいのか。0と1以外を入れたらどうなるんだろうか。いや、どうってことないのかもしれないけど。むしろint1つとBool[32]が同じというほうがメモリが節約できていいし、ビットマスクとかビットシフトをマクロでゴリゴリ定義するようなCのコードってマシになるんじゃないのかしらとか。

書式の問題として構造体の中では、

int x, y;  (a)

int x;
int y;     (b)

(a)より(b)のほうが好ましいというのに説得力を感じるのだけど、一般的ではないのかな。ちょっとほかのソースコードの.hも眺めてみよう。

と思って、include/linux/fs.hとかを見たら、inodeの構造体がやっぱりでかいことに気づいた。

struct inode {
        struct hlist_node       i_hash;
        struct list_head        i_list;
        struct list_head        i_sb_list;
        struct list_head        i_dentry;
        unsigned long           i_ino;
        atomic_t                i_count;
        unsigned int            i_nlink;
        uid_t                   i_uid;
        gid_t                   i_gid;
        dev_t                   i_rdev;
        unsigned long           i_version;
        loff_t                  i_size;
#ifdef __NEED_I_SIZE_ORDERED
        seqcount_t              i_size_seqcount;
#endif
        struct timespec         i_atime;
        struct timespec         i_mtime;
        struct timespec         i_ctime;
        unsigned int            i_blkbits;
        blkcnt_t                i_blocks;
        unsigned short          i_bytes;
        umode_t                 i_mode;
        spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
        struct mutex            i_mutex;
        struct rw_semaphore     i_alloc_sem;
        const struct inode_operations   *i_op;
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
        struct super_block      *i_sb;
        struct file_lock        *i_flock;
        struct address_space    *i_mapping;
        struct address_space    i_data;
#ifdef CONFIG_QUOTA
        struct dquot            *i_dquot[MAXQUOTAS];
#endif
        struct list_head        i_devices;
        union {
                struct pipe_inode_info  *i_pipe;
                struct block_device     *i_bdev;
                struct cdev             *i_cdev;
        };

なるほど、世の中、こんな感じにできてるのか。ってなるほどとかいうようなことじゃないかもしれないけど。で、atime、mtime、ctimeあたりの宣言を見ると、やっぱり型は同じでも行は分けて書いてある。些細な問題か。

それにしても、このinodeみたいに巨大(?)な構造体のサイズと、メモリ上のアライメントの様子ってどんな感じなのだろうか。

Linuxのヘッダファイルや、いくつかの基本的と思われるsched.c、fs.cあたりを眺めてみたけど、すぐに迷子になった。あっちこっちに定義が連鎖してたりして、どうもunsignedなintのことだと思われる「__u32」とか、結局grepしてみても一体どこで定義されてるのかすら分からなかった。