超かんたん、自作ウィンドウマネージャの作り方



今回は、ウィンドウマネージャを自作してみたいと思います。
前回、ウィンドウマネージャが無くてもXサーバーが動くことを話題にしました、その続編となっていますので、[前回「Xウィンドウマネージャっているの？」](https://qiita.com/ai56go/items/1b8bfeede2b467ac0667 )も合わせてご覧ください。

　使用OS:ArchLinux(VirtualBoxゲスト)

# 前提
下記のパッケージ等は、事前にインストールされているという前提で話を進めます。
・前回インストールしたパッケージ
・gcc
・wget
・nano(などのテキストエディター)

前回同様、ttyコンソールの状態から話を進めます。

# TinyWM
とても小さなウィンドウマネージャとして知られていて、ソースコードは以下の通りです。

```C:tinywm.c
/* TinyWM is written by Nick Welch <mack@incise.org>, 2005.
 *
 * This software is in the public domain
 * and is provided AS IS, with NO WARRANTY. */

#include <X11/Xlib.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main()
{
    Display * dpy;
    Window root;
    XWindowAttributes attr;
    XButtonEvent start;
    XEvent ev;

    if(!(dpy = XOpenDisplay(0x0))) return 1;

    root = DefaultRootWindow(dpy);

    XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root,
            True, GrabModeAsync, GrabModeAsync);
    XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
            GrabModeAsync, None, None);
    XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
            GrabModeAsync, None, None);

    for(;;)
    {
        XNextEvent(dpy, &ev);
        if(ev.type == KeyPress && ev.xkey.subwindow != None)
            XRaiseWindow(dpy, ev.xkey.subwindow);
        else if(ev.type == ButtonPress && ev.xbutton.subwindow != None)
        {
            XGrabPointer(dpy, ev.xbutton.subwindow, True,
                    PointerMotionMask|ButtonReleaseMask, GrabModeAsync,
                    GrabModeAsync, None, None, CurrentTime);
            XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr);
            start = ev.xbutton;
        }
        else if(ev.type == MotionNotify)
        {
            int xdiff, ydiff;
            while(XCheckTypedEvent(dpy, MotionNotify, &ev));
            xdiff = ev.xbutton.x_root - start.x_root;
            ydiff = ev.xbutton.y_root - start.y_root;
            XMoveResizeWindow(dpy, ev.xmotion.window,
                attr.x + (start.button==1 ? xdiff : 0),
                attr.y + (start.button==1 ? ydiff : 0),
                MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
                MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
        }
        else if(ev.type == ButtonRelease)
            XUngrabPointer(dpy, CurrentTime);
    }
}
```

http://incise.org/tinywm.html のソースコードをそのままコピペしたものです。

ttyコンソールではコピペできません。もし、インターネットからダウンロードするのなら、以下の方法があります。

```
mkdir ~/work1
cd ~/work1
wget https://raw.githubusercontent.com/mackstann/tinywm/master/tinywm.c
```

# ソースコードの変更
ソースコードの中に三ヵ所、 Mod1Mask があります、これはAltキーを意味します。
VirtualBoxゲストだと、ホスト側がAltキーを優先的に使用してしまい、うまくいきません。
三ヵ所とも ControlMask に変えてください。

```
nano tinywm.c
#テキストエディターで編集
```

# 次に、コンパイル
```
gcc -o tinywm tinywm.c -lX11
```
tinywmというファイルが出来上がっているはずです。これが出来立てほやほやのウィンドウマネージャです。

# そして、Xサーバ起動
Xサーバ起動直後、ウィンドウマネージャ(tinywm)とxtermが実行されるように設定します。

```
echo "~/work1/tinywm& xterm" > ~/.xinitrc
#注意、.xinitrcが存在する場合、上書きします。
```

Xサーバを起動するために次のコマンドを打ちます。

```
startx
```

![VirtualBox_ArchLinux_26_01_2020_09_28_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202736/b352cd78-3a02-139a-1b77-3b59098a7787.png)

機能は、
・Ctrl+左クリックして移動(もともとは、Alt+左クリック)
・Ctrl+右クリックしてサイズ変更(もともとは、Alt+右クリック)
・Ctrl+F1キーで前面に表示(もともとは、Alt+F1)
しかありませんが、前回「Xウィンドウマネージャっているの？」では移動・サイズ変更するためにコマンドを入力していたことに比べれば、はるかに快適です。

# Xサーバを終わらせる

前回と同様、最初(.xinitrc)に実行したxtermで

```
exit
```
と打てばXサーバは終了し、最初の画面に戻ります。

# 最後に
今回、ウィンドウマネージャをソースファイルからコンパイルし、実行しました。
ソースファイルは50行ほどの短いものです、実用的ではありませんがウィンドウマネージャを「厳かで近寄りがたい」と感じていた方にとっては、普通のXアプリと違いがあまりないことに気づき、親近感が持てるようになったのではないでしょうか。