X11: ICCCM (1) - テキスト

ICCCM
X のデスクトップに関する、基本的なクライアント間通信の仕様は、「Inter-Client Communication Conventions Manual (ICCCM)」と「Extended Window Manager Hints (NetWM)」によって定義されています。

ウィンドウマネージャと各クライアントの双方が、この仕様に基づく方法で実装を行うことにより、拡張されたデスクトップ操作を実現することができます。

NetWM は ICCCM を拡張する形で定義されているので、まずは ICCCM について説明します。

ICCCM の仕様はこちらにあります。
https://www.x.org/releases/current/doc/xorg-docs/icccm/icccm.html
プロパティ
クライアントは、ICCCM が定義しているプロパティを、あらかじめウィンドウにセットしておくことで、自身が望むウィンドウ動作を、ウィンドウマネージャに通知することができます。
設定されていないプロパティに関しては、ウィンドウマネージャによって、デフォルトの値が使用されます。

この場合のプロパティは、一度の XChangeProperty() で、mode = PropModeReplace によってセットする必要があります。
大きなサイズのデータを除き、複数回に分割してデータを送るようなことは、基本的にしないでください。
(ウィンドウマネージャ側で、PropertyNotify イベントにより、プロパティの変更を監視している場合があるので、一度のプロパティ変更で、すべてのデータを取得できるようにする必要があります)
TEXT タイプの文字列
ウィンドウのタイトルバーに表示する文字列などでは、TEXT タイプのプロパティを設定する必要があります。

この場合、TEXT は、複数のエンコーディングに対応した、一般的な文字列を示すものです。
実際のプロパティのタイプは、エンコーディングによって、"STRING", "COMPOUND_TEXT" などになります。

TEXT タイプで設定すると明示された文字列は、XmbTextListToTextProperty() などで値をセットした XTextProperty 構造体を使って、XSetTextProperty 関数でプロパティに設定する必要があります。

以下の関数や構造体は、<X11/Xutil.h> で定義されています。
XTextProperty 構造体
typedef struct {
    unsigned char *value; //プロパティデータ
    Atom encoding; //エンコーディング (プロパティのタイプ)
    int format;    //8,16,32
    unsigned long nitems; //データの個数
} XTextProperty;

TEXT タイプ用のプロパティデータです。
解放時は、value ポインタを XFree() で解放する必要があります。

複数の文字列がある場合は、ヌル区切りになります。
エンコーディングによって、実際のデータは異なります。
文字列から、XTextProperty 構造体にデータをセット
int XmbTextListToTextProperty(Display *display, char **list, int count,
    XICCEncodingStyle style, XTextProperty *text_prop_return);

int XwcTextListToTextProperty(Display *display, wchar_t **list, int count,
    XICCEncodingStyle style, XTextProperty *text_prop_return);

//XFree86 拡張
int Xutf8TextListToTextProperty(Display *display, char **list, int count,
    XICCEncodingStyle style, XTextProperty *text_prop_return);

typedef enum {
    XStringStyle,         // STRING (ラテン文字)
    XCompoundTextStyle,   // COMPOUND_TEXT (マルチエンコーディング)
    XTextStyle,           // TEXT (現在のロケール)
    XStdICCTextStyle,     // STRING。変換できなければ COMPOUND_TEXT
    XUTF8StringStyle      // UTF8_STRING (XFree86 による拡張)
} XICCEncodingStyle;

#define XNoMemory           -1
#define XLocaleNotSupported -2
#define XConverterNotFound  -3

Xmb は現在のロケールでのマルチバイト文字列のリストから、Xwc はワイド文字列のリストから、プロパティを設定するための XTextProperty 構造体にデータをセットします。
※Xmb の場合は、あらかじめ C ロケールをセットしている必要があります。

Xutf8 は、XFree86 による拡張で、UTF-8 文字列から変換します。X.Org の実装でも使用できますが、他の実装で使えるかはわかりません。

list文字列のリスト (char * の配列)
count文字列の数
style変換後のエンコーディングタイプ
戻り値Success で成功。
XNoMemory でメモリが足りない。
XLocaleNotSupported で、現在のロケールが X でサポートされていない。

基本的に、通常のラテン文字しか含まれていなければ XStringStyle、日本語などを含む場合は、マルチエンコーディングに対応している XCompoundTextStyle、自動で判断させたい場合は XStdICCTextStyle を指定します。
XTextProperty 構造体の値をプロパティにセット
void XSetTextProperty(Display *display, Window w, XTextProperty *text_prop, Atom property);

XTextProperty 構造体の値を元に、ウィンドウのプロパティにデータをセットします。
プロパティ
ICCCM で定義されている、テキストに関するプロパティには、以下があります。

なお、Xlib には、ICCCM 用のプロパティを簡単に設定するための関数が存在します (X11/Xutil.h)。
char * で文字列を指定する場合、対応しているのはラテン文字のみ (STRING タイプ) です。
WM_NAME (TEXT)
タイトルバーなどに表示される、アプリケーションの名前。

void XSetWMName(Display *display, Window w, XTextProperty *text_prop);
void XStoreName(Display *display, Window w, char *window_name);
WM_ICON_NAME (TEXT)
アイコン化されている時に表示される、アプリケーションの名前。
最小化している時に表示されるとは限りません。

void XSetWMIconName(Display *display, Window w, XTextProperty *text_prop);
void XSetIconName(Display *display, Window w, char *icon_name);
WM_CLIENT_MACHINE (TEXT)
サーバーを実行しているマシンから見た時の、クライアントを実行しているマシンの名前。
Unix 系であれば、gethostname() で取得できるホスト名を指定します。

void XSetWMClientMachine(Display *display, Window w, XTextProperty *text_prop);
WM_CLASS (STRING)
WM_CLASS プロパティには、ヌルで終わる2つの文字列を設定します。

XSetClassHint(Display *display, Window w, XClassHint *class_hints);

typedef struct {
    char *res_name;
    char *res_class;
} XClassHint;

1つ目がアプリケーションのリソース名、2つ目がアプリケーションのクラス名です。

リソース名は、X リソースから値を取得する時に使われます。

基本的には、どちらもアプリケーション名を設定すれば問題ありません。
プログラム
WM_NAME と WM_ICON_NAME のテキストをセットします。

ソースコードは UTF-8 であるとみなして、Xutf8TextListToTextProperty() を使用しています。

<d02-text.c>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "util.h"

/* 文字列から TEXT タイプのプロパティをセット */

static void _set_text_prop(Display *disp,Window win,Atom prop,const char *str)
{
    XTextProperty text;

    if(Xutf8TextListToTextProperty(disp, (char **)&str, 1, XCompoundTextStyle, &text) == Success)
    {
        XSetTextProperty(disp, win, &text, prop);
    
        XFree(text.value);
    }
}

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

    win = create_test_window(disp, ButtonPressMask);

    _set_text_prop(disp, win, XInternAtom(disp, "WM_NAME", False), "テスト(Normal)");

    _set_text_prop(disp, win, XInternAtom(disp, "WM_ICON_NAME", False), "テスト(Icon)");

    //イベント

    XMapWindow(disp, win);

    eventloop_button_quit(disp);

    XCloseDisplay(disp);

    return 0;
}

問題がなければ、ウィンドウのタイトルバーなどに、指定されたテキストが表示されていると思います。

NetWM の場合は、プロパティに UTF-8 の文字列をセットすることができるので、通常はそちらを使います。
ただし、NetWM を実装していないウィンドウマネージャに対応するためには、この方法でも設定しておく必要があります。