X11: NetWM (8) - ルートウィンドウへの ClientMessage

_NET_CLOSE_WINDOW
他のウィンドウに対して、WM_DELETE_WINDOW を送るなどの、ウィンドウマネージャを介したウィンドウを閉じる処理を行いたい場合は、ルートウィンドウに XSendEvent() で ClientMessage を送ります。

[ClientMessage]
window = 閉じるウィンドウ
message_type = (Atom) _NET_CLOSE_WINDOW
format = 32
data.l[0] = タイムスタンプ
data.l[1] = 通常時 1、ページャ時 2
_NET_WM_MOVERESIZE
クライアントが、任意のタイミングで、ウィンドウの移動やリサイズを開始したい場合、ルートウィンドウに XSendEvent() で ClientMessage を送ります。

タイトルバーをドラッグしてウィンドウを移動したり、ウィンドウの枠をドラッグしてリサイズするのと同じ方法で、ウィンドウマネージャによる、ウィンドウの位置移動やリサイズの動作を行わせることができます。

window = 対象のウィンドウ
message_type = (Atom) _NET_WM_MOVERESIZE
format = 32
data.l[0] = x_root (ルート座標)
data.l[1] = y_root
data.l[2] = direction
data.l[3] = button (ボタン番号)
data.l[4] = 通常時 1、ページャ時 2

//direction
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
#define _NET_WM_MOVERESIZE_SIZE_TOP          1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
#define _NET_WM_MOVERESIZE_MOVE              8  //移動
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9  //キーボードによるサイズ変更
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10  //キーボードによる位置移動
#define _NET_WM_MOVERESIZE_CANCEL           11  //キャンセル
グラブの解除
処理を開始する前に、クライアントが行っている、ポインタ/キーボードのグラブは解除しておく必要があります。

処理が開始した場合、ウィンドウマネージャによってグラブが行われます。
他のクライアントによってグラブが行われている場合は、グラブ中のクライアント以外がグラブを終了したりできないので、もし処理の開始時点で、すでにグラブが行われていた場合は、処理が失敗します。

クライアント側で何もグラブを行っていなかったとしても、ButtonPress イベントが来た時は、X サーバーによる自動グラブがすでに開始されている状態であるため、ClientMessage を送信する前は、常に XUngrabPointer() を実行する必要があります。
これによって、自動グラブも解除することができます。
ボタン押し時に開始
ButtonPress イベントによって処理を開始したい場合は、x_root, y_root でポインタのルート座標を指定し、button で押されたボタンの番号を指定する必要があります。

direction では、ウィンドウ位置の移動なのか、また、リサイズの場合は、どの端を基準にしてリサイズするかを指定します。

button で指定されたボタンが離されると、処理が終了します。
キー押し時に開始
KeyPress イベントによって処理を開始したい場合は、direction に _NET_WM_MOVERESIZE_SIZE_KEYBOARD か _NET_WM_MOVERESIZE_MOVE_KEYBOARD を指定します。
それ以外の値 (x_root, y_root, button) は無視されます。
プログラム
左ボタンドラッグで、ウィンドウ位置を移動します。
右ボタンドラッグで、ウィンドウのリサイズ (右下) を行います。

閉じるボタンが押されると、終了します。

$ cc -o run d15-moveresize.c util.c -lX11

<d15-moveresize.c>
#include <string.h>
#include <X11/Xlib.h>
#include "util.h"

#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
#define _NET_WM_MOVERESIZE_SIZE_TOP          1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
#define _NET_WM_MOVERESIZE_MOVE              8  //移動
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9  //キーボードによるサイズ変更
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10  //キーボードによる位置移動
#define _NET_WM_MOVERESIZE_CANCEL           11  //キャンセル

/* 位置移動・リサイズを開始 */

static void _moveresize(Window win,int direction,int x_root,int y_root,int button)
{
    XClientMessageEvent ev;

    //自動グラブを解除
    XUngrabPointer(g_disp.disp, CurrentTime);

    memset(&ev, 0, sizeof(XClientMessageEvent));

    ev.type = ClientMessage;
    ev.message_type = GET_ATOM("_NET_WM_MOVERESIZE");
    ev.window = win;
    ev.format = 32;
    ev.data.l[0] = x_root;
    ev.data.l[1] = y_root;
    ev.data.l[2] = direction;
    ev.data.l[3] = button;
    ev.data.l[4] = 1;

    XSendEvent(g_disp.disp, g_disp.root, False,
        SubstructureNotifyMask | SubstructureRedirectMask,
        (XEvent *)&ev);
}

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

    set_display(disp);

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

    //イベント

    XMapWindow(disp, win);

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

        if(event_quit(&ev)) break;

        switch(ev.type)
        {
            case ButtonPress:
                if(ev.xbutton.button == Button1)
                {
                    _moveresize(win, _NET_WM_MOVERESIZE_MOVE,
                        ev.xbutton.x_root, ev.xbutton.y_root, ev.xbutton.button);
                }
                else if(ev.xbutton.button == Button3)
                {
                    _moveresize(win, _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT,
                        ev.xbutton.x_root, ev.xbutton.y_root, ev.xbutton.button);
                }
                break;
        }
    }

    XCloseDisplay(disp);

    return 0;
}