X11: NetWM (2) - クライアントウィンドウのプロパティ

文字列プロパティ
NetWM において、クライアントウィンドウに設定する文字列プロパティには、以下のものがあります。

_NET_WM_NAMEウィンドウタイトルとして表示されます。
WM_NAME より優先されます。
_NET_WM_ICONアイコン化状態のタイトルとして表示されます。
WM_ICON_NAME より優先されます。
_NET_WM_VISIBLE_NAME_NET_WM_NAME とは異なるタイトルを表示したい場合。
_NET_WM_VISIBLE_ICON_NAME_NET_WM_ICON とは異なるアイコンタイトルを表示したい場合。

ICCCM では、プロパティの文字列は基本的に TEXT タイプでしたが、NetWM では、UTF-8 文字列で設定します。
(type = "UTF8_STRING", format = 8)。

※UTF-8 文字列も含め、プロパティに文字列を設定する場合、終端のヌル文字は必要ありません。
装飾フレームの幅を取得
ウィンドウの最初のマップ後、ウィンドウマネージャによって装飾フレームウィンドウが作成された後に、クライアントウィンドウの _NET_FRAME_EXTENTS プロパティを読み込むと、その装飾フレームの各幅を取得することができます。
(type = "CARDINAL", format = 32)

※CARDINAL は、整数を意味しています。

4個の数値の配列になっており、left, right, top, bottom の順で、各辺の幅 (px) が入っています。

この値を使うと、クライアントウィンドウの左上のルート座標から、フレームウィンドウの左上のルート座標を計算できます。
ウィンドウのアイコン
タイトルバーやタスクバーに表示されるような、ウィンドウのアイコン画像を、ARGB イメージで設定できます。

クライアントウィンドウの _NET_WM_ICON プロパティに、type = "CARDIAL", format = 32 で、イメージデータの配列を設定します。

アイコンは、複数のサイズ (幅&高さ) のイメージを、複数個設定できます。
このプロパティを読み込むウィンドウマネージャなどは、このアイコンデータの中から、適切なサイズのアイコンを取得して、表示することができます。
※必要なサイズに合うアイコンがない場合は、拡大縮小して表示される場合があります。

通常は、16〜128 px 程度の複数のアイコンをセットしますが、一つのサイズでも構いません。

一つのリクエスト (XChangeProperty) で送信できるデータサイズは制限されています。
X.Org 実装では、65535 x 4 byte までです (format = 32 のデータは、4 byte として扱います)。
つまり、256 x 256 px のアイコンイメージは、一度の XChangeProperty 関数では送れないので、注意してください。
データ
format = 32 なので、long 型の配列で、データを用意します。

※64bit OS の場合、実際に必要なデータは 32bit で十分ですが、プロパティに値をセットする時は、64bit の数値配列としてデータを用意する必要があります。

一つのサイズのアイコンごとに、以下の値をセットします。

  • 最初の値は、イメージの幅 (px)
  • 2番目の値は、イメージの高さ (px)
  • 3番目以降は、幅 x 高さのイメージデータ (ARGB)。
    32bit の 0xAARRGGBB 形式で、1px ごとに、左上から右下の順で ARGB 値を並べます。

複数のアイコンがある場合は、続けて2つ目以降のデータを追加します。
プログラム
ウィンドウタイトルとアイコンをセットします。
マップ時に、装飾フレームの幅を表示します。

アイコンを表示する時のサイズが 16x16 に近い方は赤いアイコンになり、それより大きい場合 (Alt+Tab 時など) は、青いアイコンになるのを確認してください。

$ cc -o run d09-winprop.c util.c -lX11

<d09-winprop.c>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "util.h"

/* UTF8_STRING プロパティセット */

static void _setprop_utf8(Display *disp,Window win,Atom prop,const char *str)
{
    XChangeProperty(disp, win, prop,
        GET_ATOM("UTF8_STRING"), 8,
        PropModeReplace, (unsigned char *)str, strlen(str));
}

/* アイコンイメージセット */

static void _set_icon(Display *disp,Window win)
{
    unsigned long *buf,*pd;
    int i;
    Atom prop;

    prop = GET_ATOM("_NET_WM_ICON");

    buf = (unsigned long *)malloc((2 + 32 * 32) * sizeof(long));

    //16x16 (赤)

    buf[0] = buf[1] = 16;

    pd = buf + 2;
    for(i = 0; i < 16 * 16; i++)
        *(pd++) = 0xffff0000;

    XChangeProperty(disp, win, prop, XA_CARDINAL, 32,
        PropModeReplace, (unsigned char *)buf, 2 + 16 * 16);

    //32x32 を追加 (青、半透明)

    buf[0] = buf[1] = 32;

    pd = buf + 2;
    for(i = 0; i < 32 * 32; i++)
        *(pd++) = 0x800000ff;

    XChangeProperty(disp, win, prop, XA_CARDINAL, 32,
        PropModeAppend, (unsigned char *)buf, 2 + 32 * 32);

    free(buf);
}

int main(int argc,char **argv)
{
    Display *disp;
    Window win;
    XEvent ev;
    long *buf;
    int num;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    set_display(disp);

    win = create_test_window2(disp, 200, 200, 0,
        ButtonPressMask | StructureNotifyMask);

    _setprop_utf8(disp, win, GET_ATOM("_NET_WM_NAME"), "タイトル");
    _setprop_utf8(disp, win, GET_ATOM("_NET_WM_ICON_NAME"), "ICONタイトル");
    _setprop_utf8(disp, win, GET_ATOM("_NET_WM_VISIBLE_NAME"), "タイトル[1]");
    _setprop_utf8(disp, win, GET_ATOM("_NET_WM_VISIBLE_ICON_NAME"), "ICONタイトル[1]");

    _set_icon(disp, win);

    //イベント

    XMapWindow(disp, win);

    while(1)
    {
        XNextEvent(disp, &ev);

        if(event_quit(&ev)) break;

        switch(ev.type)
        {
            case MapNotify:
                buf = (long *)read_prop32(win, GET_ATOM("_NET_FRAME_EXTENTS"), XA_CARDINAL, &num);
                if(buf)
                {
                    printf("[_NET_FRAME_EXTENTS] left:%ld, right:%ld, top:%ld, bottom:%ld\n",
                        buf[0], buf[1], buf[2], buf[3]);
                    
                    XFree(buf);
                }
                break;
        }
    }

    XCloseDisplay(disp);

    return 0;
}
_NET_WM_STRUT/_NET_WM_STRUT_PARTIAL
タスクバーやランチャなど、画面の一部を占領するようなアプリケーションを作りたい場合は、そのウィンドウに _NET_WM_STRUT または _NET_WM_STRUT_PARTIAL プロパティを設定して、ウィンドウが画面内で占領する領域を指定する必要があります。
(type = "CARDINAL", format = 32)

_NET_WM_STRUT_PARTIAL は、_NET_WM_STRUT よりも後に定義されており、追加の値を指定することができます。
_NET_WM_STRUT_PARTIAL をサポートしていないウィンドウマネージャのために、_NET_WM_STRUT も同時に設定することができます。

_NET_WM_STRUT4個の配列。
left, right, top, bottom
_NET_WM_STRUT_PARTIAL12個の配列。
left, right, top, bottom,
left_start_y, left_end_y, right_start_y, right_end_y,
top_start_x, top_end_x, bottom_start_x, bottom_end_x

left, right, top, bottom は、画面のそれぞれの端で確保する幅です。
*_x, *_y は、それぞれの端で、どの位置からどの位置までを占領するかの座標です。