X11: ICCCM (3) - WM_HINTS, WM_NORMAL_HINTS

WM_HINTS
ウィンドウの WM_HINTS プロパティに、type = "WM_HINTS", format = 32 で各値を設定すると、ウィンドウの動作などに関する情報を、ウィンドウマネージャに伝えることができます。

プロパティが設定されていない場合は、ウィンドウマネージャによって、デフォルトの値が使用されます。

基本的に、ウィンドウを最初にマップする前に設定します。
関数と構造体
Xlib には、このプロパティを設定するための構造体と関数があるので、通常はそちらを使います (X11/Xutil.h)。

typedef struct {
    long flags;
    Bool input;
    int initial_state;
    Pixmap icon_pixmap;
    Window icon_window;
    int icon_x, icon_y;
    Pixmap icon_mask;
    XID window_group;
} XWMHints;

//flags
#define InputHint         (1L << 0)
#define StateHint         (1L << 1)
#define IconPixmapHint    (1L << 2)
#define IconWindowHint    (1L << 3)
#define IconPositionHint  (1L << 4)
#define IconMaskHint      (1L << 5)
#define WindowGroupHint   (1L << 6)
#define AllHints (InputHint|StateHint|IconPixmapHint\
    |IconWindowHint|IconPositionHint|IconMaskHint|WindowGroupHint)
#define XUrgencyHint      (1L << 8)

//initial_state
#define WithdrawnState 0 //完全非表示
#define NormalState 1    //通常表示
#define IconicState 3    //アイコン化

XWMHints *XAllocWMHints(void);

int XSetWMHints(Display *display, Window w, XWMHints *wm_hints);

XWMHints *XGetWMHints(Display *display, Window w);

XWMHints 構造体のメンバは、将来的に増える可能性があるということで、XAllocWMHints() で確保してから使用することになっていますが、今後メンバが増えるようなことは、おそらくないと思います。
確保した構造体は、ゼロクリアされています。使用後は XFree() で解放します。

XSetWMHints() で、WM_HINTS プロパティに XWMHints 構造体の値をセットします。
プロパティのデータは、format = 32 の値の配列になります。

XGetWMHints() で、WM_HINTS プロパティから値を取得します。
XWMHints 構造体
flags使用するメンバのマスクとフラグ。

XUrgencyHint が ON の場合、ウィンドウに注意を引きつけるために、ウィンドウマネージャがウィンドウに対して何らかのアクションをします (フレームを点滅させるなど)。
inputウィンドウマネージャの制御によって、入力フォーカスが設定されるか。

True の場合、ウィンドウマネージャが XSetInputFocus() を行うことで、適切なウィンドウに入力フォーカスがセットされます。
False の場合、クライアントが明示的に XSetInputFocus() を使用しない限り、入力フォーカスがセットされることはありません。
initial_stateWithdrawn 状態からマップされる時のウィンドウ状態。
icon_*アイコン化状態に関する値。
window_groupグループリーダーのウィンドウ。詳細は後述します。
プログラム (1)
最初のマップ時は、アイコン化状態で表示し、ウィンドウは入力フォーカスを全く受け取りません。

ポインタボタンを押すと終了します。

$ cc -o run d04a-wmhints.c util.c -lX11

<d04a-wmhints.c>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "util.h"

int main(int argc,char **argv)
{
    Display *disp;
    Window win;
    XWMHints *wmh;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    win = create_test_window(disp, ButtonPressMask);

    //WM_HINTS

    wmh = XAllocWMHints();

    wmh->flags = InputHint | StateHint;
    wmh->input = False;
    wmh->initial_state = IconicState;

    XSetWMHints(disp, win, wmh);

    XFree(wmh);

    //イベント

    XMapWindow(disp, win);

    eventloop_button_quit(disp);

    XCloseDisplay(disp);

    return 0;
}
WM_NORMAL_HINTS
WM_NORMAL_HINTS プロパティに、type = "WM_SIZE_HINTS", format = 32 で各値を設定することで、通常状態で表示されている時のウィンドウの、位置やサイズに関する情報を指定します。
関数
XSizeHints *XAllocSizeHints(void);

void XSetWMNormalHints(Display *display, Window w, XSizeHints *hints);

Status XGetWMNormalHints(Display *display, Window w, XSizeHints *hints_return, long *supplied_return);

WM_HINTS の場合と同様に、XSizeHints 構造体を確保して、XSetWMNormalHints() でプロパティをセットします。
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; //分子
        int y; //分母
    } min_aspect, max_aspect;
    int base_width, base_height;
    int win_gravity;
} XSizeHints;

//flags
#define USPosition      (1L<<0)
#define USSize          (1L<<1)
#define PPosition       (1L<<2)
#define PSize           (1L<<3)
#define PMinSize        (1L<<4)
#define PMaxSize        (1L<<5)
#define PResizeInc      (1L<<5)
#define PAspect         (1L<<6)
#define PBaseSize       (1L<<8)
#define PWinGravity     (1L<<9)
#define PAllHints       (PPosition|Psize|PMinSize|PMaxSize|PResizeInc|PAspect)

flags使用するメンバのマスクとフラグ
x,y,width,height※廃止
min_width
min_height
最小ウィンドウサイズ
max_width
max_height
最大ウィンドウサイズ
width_inc
height_inc
ウィンドウサイズを計算する時の段階値
min_aspect
max_aspect
ウィンドウサイズのアスペクト比の範囲
base_width
base_height
ウィンドウサイズを計算する時の基本値
win_gravityウィンドウマネージャが作成したフレームウィンドウの基準位置。
ウィンドウ属性の win_gravity で設定できる値と同じ値。

ウィンドウの初期位置とサイズ
フラグに USPosition, USSize がある場合、ウィンドウの位置またはサイズは、ユーザーによって指定されたことを示します。
PPosition, PSize がある場合、ウィンドウの位置またはサイズは、プログラムによって指定されたことを示します。

このフラグが設定されていると、ウィンドウマネージャは、Withdrawn 状態からマップされた時に、ウィンドウの現在の位置やサイズをそのまま使用して表示します (ウィンドウマネージャの実装にもよる)。

例えば、前回のウィンドウ位置とサイズを復元したい場合などは、このフラグを設定すると、最初のマップ時に表示される位置やサイズを固定できます。

ウィンドウのサイズ
ウィンドウのサイズは、各値を使って計算されます。

アスペクト比の指定がない場合、以下のように計算されます。

width = base_width + i * width_inc
height = base_height + j * height_inc

さらに、min と max のサイズで値が制限されます。

base の値がない場合は、min のサイズが使用されます。
min の値がない場合は、base のサイズが使用されます。

アスペクト比の指定がある場合は、ウィンドウサイズがその比率の範囲になるように調整されます。
その場合、base の値がある場合は、そのサイズを引いてからアスペクト比の計算を行います。
min_aspect と max_aspect で同じ値を指定すると、アスペクト比を固定できます。
固定ウィンドウサイズ
min_width = max_width, min_height = max_height の場合、ウィンドウサイズは固定となり、ユーザーによってリサイズができない状態となります。

この場合、ウィンドウ装飾が変更される場合があります (ウィンドウ枠に、リサイズを行うための領域がある場合、それがなくなる)。
プログラム (2)
実行時、引数に "0" を渡すか、指定しない場合、各ウィンドウサイズの指定を行います。
引数に "1" を渡した場合、アスペクト比を 4:3 に固定します。

ポインタボタンを押すと終了します。

"0" 時、ウィンドウサイズをリサイズすると、50 px 単位でサイズが変わり、50〜500 px の範囲までしかリサイズできないことを確認してください。また、ウィンドウ位置が (100, 100) で固定されていることも確認してください。

"1" 時、ウィンドウサイズは常に 4:3 のアスペクト比になっていることを確認してください。

(ウィンドウマネージャの実装によって、対応している場合と対応していない場合があります)

$ cc -o run d04b-normhints.c util.c -lX11

<d04b-normhints.c>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "util.h"

int main(int argc,char **argv)
{
    Display *disp;
    Window win;
    XSizeHints *sh;
    int type;

    if(argc < 2)
        type = 0;
    else
        type = atoi(argv[1]);
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    win = create_test_window(disp, ButtonPressMask);

    //WM_NORMAL_HINTS

    sh = XAllocSizeHints();

    switch(type)
    {
        //位置固定, min(50x50), max(500x500), 50 ずつサイズが変化
        case 0:
            XMoveWindow(disp, win, 100, 100);

            sh->flags = PPosition | PMinSize | PMaxSize | PResizeInc;
            sh->min_width = sh->min_height = 50;
            sh->max_width = sh->max_height = 500;
            sh->width_inc = sh->height_inc = 50;
            break;
        //アスペクト比を 4:3 で固定
        default:
            sh->flags = PAspect;
            sh->min_aspect.x = 4, sh->min_aspect.y = 3;
            sh->max_aspect.x = 4, sh->max_aspect.y = 3;
            break;
    }

    XSetWMNormalHints(disp, win, sh);

    XFree(sh);

    //イベント

    XMapWindow(disp, win);

    eventloop_button_quit(disp);

    XCloseDisplay(disp);

    return 0;
}