LECTURE 13 ウィンドウマネージャとのやりとり

○ はじめに

ここまでの LECTURE で作成したサンプルプログラムの画像を見ると、タイトルバーに "Untitled" と描かれています。自分のアプリケーションを作った時にそのタイトルが "Untitled" では悲しいですね。。。ところでこのタイトルバーはウィンドウマネージャによってつけられたものなので、指定したタイトルをタイトルバーに表示するためには、プログラムからウィンドウマネージャに必要な情報を教えなければいけません。

ここではタイトルバーに表示する文字列に限らず、様々な情報をウィンドウマネージャとやりとりする方法について説明していきます。

○ サンプルプログラム(タイトル文字列の表示、XSetWMProperties() )

タイトルバーに "Sample Program!" と表示するサンプルプログラム xwm.c です。下図において、指定した文字列がちゃんとタイトルバーに表示されていることが分かります。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

013wm.jpg

--- xwm.c ---

    1	#include <X11/Xlib.h> 
    2	#include <X11/Xutil.h>  /* for XTextProperty */
    3	#include <X11/Xatom.h>  /* for XA_STRING     */
    4	#include <string.h>
    5	
    6	
    7	#define WIN_W   400  /* ウィンドウの幅   */
    8	#define WIN_H   300  /* ウィンドウの高さ */
    9	#define WIN_X   100  /* ウィンドウ表示位置(X) */
   10	#define WIN_Y   100  /* ウィンドウ表示位置(Y) */
   11	#define BORDER  2    /* ボーダの幅      */
   12	
   13	
   14	int 
   15	main( int argc, char** argv )
   16	{
   17		Display*       dpy;          /* ディスプレイ */
   18		Window         root;         /* ルートウィンドウ */
   19		Window         win;          /* 表示するウィンドウ */
   20		int            screen;       /* スクリーン */
   21		unsigned long  black, white; /* 黒と白のピクセル値 */
   22		XEvent         evt;
   23		XTextProperty  w_name;       /* ウィンドウ名などを設定する */
   24	
   25		dpy = XOpenDisplay( "" );
   26	
   27		root   = DefaultRootWindow( dpy );
   28		screen = DefaultScreen( dpy );
   29		white  = WhitePixel( dpy, screen );
   30		black  = BlackPixel( dpy, screen );
   31	
   32		win = XCreateSimpleWindow( dpy, root,
   33			   WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, black, white );
   34	
   35		/* ウィンドウ名の指定 */
   36		w_name.value    = (unsigned char*)"Sample program!";
   37		w_name.encoding = XA_STRING;
   38		w_name.format   = 8;
   39		w_name.nitems   = strlen( "Sample program!" );
   40	
   41		/* ウィンドウマネージャに情報を通知する */
   42		XSetWMProperties( dpy, win, &w_name, NULL, argv, argc, NULL, NULL, NULL );
   43	
   44		XSelectInput( dpy, win, KeyPressMask );
   45		XMapWindow( dpy, win );
   46	
   47		while( 1 ) {
   48			XNextEvent( dpy, &evt );
   49	      
   50			switch ( evt.type ) {
   51				case KeyPress :
   52					/* リソースの解放 */
   53					XDestroyWindow( dpy, win );
   54					XCloseDisplay( dpy );
   55					return 0;
   56			}
   57		}
   58	}
   59	
   60	/* End of xwm.c */

○ サンプルプログラムの説明

では、サンプルプログラムについて説明していきます。

  • 2 〜 3 行目

    ウィンドウマネージャとタイトル名などをやりとりする際に使用する XTextProperty 構造体と、 XTextProperty 構造体のメンバに設定する定数の定義を参照するためのヘッダファイルをインク ルードしています。

  • 23 行目

    XTextProperty 構造体の変数を宣言しています。この変数に必要な情報を設定して、ウィンドウ マネージャに通知します。

  • 35 〜 39 行目

    まず、XTextProperty 構造体変数の w_name に値を設定しています。XTextProperty 構造体は、 次のように定義されています。

      typedef struct {
        unsigned char* value;    /* 文字列 */
        Atom           encoding; /* XA_STRING を指定 */
        int            format;   /* 半角英数字を設定する場合、8 を指定
                                   ( 8 ビットのデータ配列として扱う ) */
        unsigned long  nitems;   /* value の文字数 */
      } XTextProperty;
    
    

    上の内容に従って、"Sample program!" という文字列についての情報を設定しています。

    設定した値をウィンドウのタイトルバーに表示してもらうために、ウィンドウマネージャに通知 する必要がありますが、これには XSetWMProperties() を使っています。

      void XSetWMProperties( Display*       dpy,
                             Window         w,
                             XTextProperty* window_name,
                             XTextProperty* icon_name,
                             char**         argv,
                             int            argc,
                             XSizeHints*    notmal_hints,
                             XWMHints*      wm_hints,
                             XClassHint*    class_hints
                           );
    
      window_name  : ウィンドウ名
      icon_name    : アイコン名
      argv         : アプリケーションの引数
      argc         : 引数の個数
      normal_hints : ウィンドウの大きさ
      wm_hints     : WMヒント
      class_hints  : クラスヒント
    
    

    上の各引数について、どのような情報を与えることができるのかを以下に示します。

    • window_name

      ウィンドウ名(タイトルバーに表示する文字列)を指定します。設定の方法は、サンプルプログ ラムを参照してください。

    • icon_name

      アイコン化された場合に使用する名前を、window_name と同様の方法で指定します。

    • argc, argv

      main( argc, argv ) で指定されたものをそのまま渡します。

    • normal_hints

      ウィンドウの大きさ、位置、リサイズの単位、ウィンドウの縦横比といった情報をノーマルヒ ントといいます。ノーマルヒントは XSizeHints 構造体を使って設定します。

          #include <X11/Xutil.h> /* XSizeHints */
      
          typedef struct
          {
            long   flags;                 /* どのメンバに値を指定したか */
            int    x, y;                  /* ウィンドウの位置 */
            int    width, height;         /* ウィンドウの幅と高さ */
            int    min_width, min_height; /* 最小サイズ */
            int    max_width, max_height; /* 最大サイズ */
            int    width_inc, height_inc; /* リサイズ時の増分 */
      
            struct
            {
              int x; /* ウィンドウの最小(大)の縦横比(x:y) */
              int y;
            }min_aspect, max_aspect;
          } XSizeHints;  
      
      

      flags にはどのメンバに値を設定したかを設定します。flags に設定する値とメンバの対応は 以下の通りです。

      構造体のメンバ flags
      x, y USPosition または PPosition
      width, height USSize または PSize
      min_width, min_height PMinSize
      max_width, max_height PMaxSize
      width_inc, height_inc PResizeInc
      min_aspect, max_aspect PAspect

    • wm_hints

      クライアントのキーボードフォーカスモデル、初期動作状態(標準、もしくはアイコン) 、 アイコンに使うピクスマップやウィンドウ、アイコンの初期位置などを設定します。 設定は XWMHints 構造体を使って行います。

          #include <X11/Xutil.h> /* XWMHints */
      
          typedef struct
          {
            long   flags;            /* どのメンバに値を指定したか */
            Bool   input;            /* クライアントが使用するキーボードフォーカスモデル */
            int    initial_state;    /* 初期状態が標準かアイコンか */
            Pixmap icon_pixmap;      /* アイコンに使われるピクスマップ */
            Window icon_window;      /* アイコンに使われるウィンドウ */
            int    icon_x, icon_y;   /* アイコンの初期表示位置 */
            Pixmap icon_mask;        /* icon_pixmap に対するマスク */
            XID    window_group;     /* ウィンドウグループに関係する ID */
          } XWMHints;  
      
      

      flags にはどのメンバに値を設定したかを設定します。flags に設定する値とメンバの対応は 下表の通りです。

      構造体のメンバflags
      input InputHint
      initial_state StateHint
      icon_pixmap IconPixmapHint
      icon_window IconWindowHint
      icon_x, icon_yIconPositionHint
      icon_mask IconMaskHint
      window_group WindowGroupHint
      全て AllHint

      input には、クライアントが使用するキーボードフォーカスモデルを指定します。 以下のいずれかを指定します。

      input 値 フォーカスモデル 説明
      True Passive Input キーボードフォーカスは設定しないがキーボード入力は必要
      True Locally Active Input キーボード入力が必要かつクライアントのウィンドウの1つが既にフォーカス を持っているときだけキーボードフォーカスを設定する
      False Globally Active Input キーボード入力が必要かつほかのクライアントのウィンドウにフォーカスが 設定されていてもキーボードフォーカスを設定する
      False No input キーボード入力を必要としない

      2008/06/02 追記。↑のキーボードフォーカスモデルに応じた input 値の設定表ですが、キーボードフォーカスモデルの説明だけ書いていて、肝心の設定値を書いていないことに今更気づきました。ご迷惑をおかけしました…

      initial_state には、初期状態が標準(ウィンドウ)なのかアイコンなのかを指定します。

      定数名状態
      WithdrawnStateすみません、どういう状態かよく分りません
      IconicStateアイコン状態
      NomalState 標準状態

      icon_pixmap はアイコンに使うピクスマップを指定します。このピクスマップは深さが 1 でな ければいけません。icon_mask は icon_pixmap のどのピクセルを表示するかを決めるビットマ ップです。icon_window には、自分のアイコンの ID を必要としたり、より複雑なビットマッ プを設定しようとする場合に、自分が生成したウィンドウを指定します。

      window_group は、1 つのクライアントが複数の最上位ウィンドウを持つ場合に使います。これ により複数の最上位ウィンドウをグループ化して、ウィンドウマネージャがアイコン化などを 行う際にグループとして扱うことができます。最上位ウィンドウの 1 つをグループリーダと決 め、そのウィンドウの window_group には自分のウィンドウ ID を設定します。その他の最上 位ウィンドウにはグループリーダの ID を設定します。

    • class_hints

      ウィンドウマネージャが、リソースデータベースからクライアントのリソースを取りだすこと ができるように、アプリケーション名とアプリケーションクラス名を指定します。アプリケー ション名は小文字で、アプリケーションクラス名は頭文字を大文字で指定します(習慣)。ただ し最初が x で始まる場合にはその次の文字も大文字にします。

          #include <X11/Xutil.h> /* XClassHint */
      
          typedef struct
          {
            char* res_name;  /* アプリケーション名 */
            char* res_class; /* アプリケーション */
          } XClassHint;  
      
      

    以上で、各引数の説明は終りです。ただしこれらの情報はあくまでヒントであって、実際にこれ らの情報を使用するかどうかはウィンドウマネージャに委ねられます。

○ タイトル文字列の設定/取得

サンプルプログラムでは、XSetWMProperties を使ってタイトル文字列を設定しましたが、以下の関数による設定/取得も出来ます。XSetWMName における text_prop の設定は、サンプルプログラムでの XSetWMProperties と同様です。

(設定)
void XSetWMName( Display* dpy, Window w, XTextProperty* text_prop );
XStoreName( Display* dpy, Window w, char* window_name );

(取得)
Status XGetWMName( Display* dpy, Window w, XTextProperty* text_prop_return );
XFetchName( Display* dpy, Window w, char** window_name_return );

--- コード例 ---

XTextProperty prop;

prop.value    = ( unsigned char* )"Sample Title";
prop.encoding = XA_STRING;
prop.format   = 8;
prop.nitems   = strlen( "Sample Title" );

XSetWMName( dpy, w, &prop );

if ( XGetWMName( dpy, w, &prop ) ) {
        /* 取得成功 */
}

○ アイコン表示時のタイトル文字列の設定/取得

アイコン表示時のタイトル文字列を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。

使い方は、タイトル文字列の設定/取得用関数と同じです。

(設定)
void XSetWMIconName( Display* dpy, Window w, XTextProperty* text_prop );
XSetIconName( Display* dpy, Window w, char* icon_name );

(取得)
Status XGetWMIconName( Display* dpy, Window w, XTextProperty* text_prop_return );
XGetIconName( Display* dpy, Window w, char** icon_name_return );

○ ノーマルヒント( 最小/最大サイズ、縦横比、リサイズ時の増分など ) の設定/取得

ノーマルヒント ( XSetWMProperties に指定する normal_hints、サンプルプログラムの説明を参照してください ) を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。

#include <X11/Xutil.h> /* XSizeHints */
void XSetWMNormalHints( Display* dpy, Window w, XSizeHints* hints );
Status XGetWMNormalHints( Display* dpy, Window w, 
    XSizeHints* hints_return, long* supplied_return );

supplied_return : ユーザ設定による値を示すビットマスク

--- コード例 ---

#include <X11/Xutil.h> /* XSizeHints */

XSizeHints hints;
long supplied_return;

hints.min_width  = 640;
hints.min_height = 480;
hints.max_width  = 640;
hints.max_height = 480;

hints.flags = PMinSize | PMaxSize;

XSetWMNormalHints( dpy, w, &hints );

if ( XGetWMNormalHints( dpy, w, &hints, &supplied_return ) ) {
        /* 取得成功 */
}

○ クラスヒント( リソース検索用のアプリケーション名とクラス名 ) の設定/取得

クラスヒント( XSetWMProperties に指定する class_hints、サンプルプログラムの説明を参照してください ) を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。

#include <X11/Xutil.h> /* XClassHint */
XSetClassHint( Display* dpy, Window w, XClassHint* class_hints );
Status XGetClassHint( Display* dpy, Window w, XClassHint* class_hints_return );

--- コード例 ---

#include <X11/Xutil.h> /* XClassHint */

XClassHint class_hints;

class_hints.res_name  = "sample";
class_hints.res_class = "Sample";

XSetClassHint( dpy, w, &class_hints );

if ( XGetClassHint( dpy, w, &class_hints ) ) {
        /* 取得成功 */
}

○ 日本語タイトルの表示( XmbSetWMProperties() )

XSetWMProperties() の 引数である window_name および icon_name に、マルチバイト文字列を指定できるようにした関数が XmbSetWMProperties() です。この関数の引数 window_name および icon_name には、XTextProperty 構造体ではなく char の配列を渡します。

void XmbSetWMProperties( Display*    dpy, 
                         Window      w, 
                         char*       window_name, 
                         char*       icon_name, 
                         char**      argv, 
                         int         argc, 
                         XSizeHints* normal_hints, 
                         XWMHints*   wm_hints, 
                         XClassHint* class_hints 
                       );

最近(2007/02/23)になって久々に Fedora6 上で試してみたら、日本語が出ませんでした… XmbSetWMProperties() を実行する前に setlocale() でロケールをきちんと設定してからだと出る様です。考えてみれば当たり前ですが。下のソースコードにも追加しておきました。

↓ソースコードは xwm_mb.c です。イベントループで何も処理していませんので、Ctrl-C で終了させてください。

013wm_mb.jpg

○ ウィンドウクローズを通知してもらう ( XSetWMProtocols() )

XSetWMProtocols() は、ウィンドウマネージャの WM_PROTOCOLS プロパティを設定するための関数です。WM_PROTOCOLS プロパティは、ある条件が成立したときにウィンドウマネージャから通知を受けたい場合に設定します。

WM_PROTOCOLS プロパティに設定できるプロパティはいくつかありますが、ここでは WM_DELETE_WINDOW について説明します。これは最上位ウィンドウの破壊要求を表すプロパティです。これを WM_PROTOCOLS プロパティに設定すると、クライアントの最上位ウィンドウ(複数ある場合はどれか 1 つでも)を終了させた場合に ClientMessage イベントを受け取ることができます。このとき ClientMesage イベントの data.l[0] というメンバに WM_DELETE_WINDOW を表すアトムが格納されています。アトム ( Atom ) とはプロパティの識別子と考えてもらえば良いです。このイベントを受け取った場合に通常の終了処理を行うようにすることで、ユーザが突然ウィンドウを終了した場合にも対応することができます。

実際の処理の流れとしては、まず WM_DELETE_WINDOW プロパティに対応したアトムを取得し、そのアトムを WM_PROTOCOLS に設定します。

アトムを取得するには、XInternAtom() を使います。

Atom XInternAtom( Display* display, char* atom_name, Bool only_if_exists );

atom_name      : アトムを取得したいプロパティの名称
only_if_exists : False ならば、アトムが存在しない場合には新しく生成される

取得したアトムを WM_PROTOCOLS に設定するには、XSetWMProtocols() を使います。

Status XSetWMProtocols( Display* display, Window w, Atom* protocols, int count );

protocol : 設定したいプロパティに対応するアトムのリスト
count    : リスト中のアトムの数

あとはイベントループの中で ClientMessage イベントを受け取って処理するコードを書くだけです。

Atom atom;
...

atom = XInternAtom( dpy, "WM_DELETE_WINDOW", True );
XSetWMProtocols( dpy, win, &atom, 1 );
...

while( 1 ) {
  XNextEvent( dpy, &event );

  switch ( event.type ) {
  ...

    case ClientMessage :
      if ( event.xclient.data.l[0] == atom ) {
        /* 終了(確認)処理 を行う */
      }

      break;
   }
}