ログイン
編集不可のページディスカッション情報添付ファイル
"clear/wm_devel/2011-08-16"の差分

MMA
3と5のリビジョン間の差分 (その間の編集: 2回)
2011-08-16 16:20:12時点のリビジョン3
サイズ: 2052
編集者: clear
コメント:
2011-08-16 22:02:50時点のリビジョン5
サイズ: 6200
編集者: clear
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 18: 行 18:
終了時に接続を切るときは`XCloseDisplay()`を使うらしい。
 XCloseDisplay:: Xサーバとの接続を切る
{{{
int XCloseDisplay(Display *display);
}}}
 * まあ見たとおり
 *
これによって全てのウィンドウと確保したリソースが解放されるらしい。プログラムは終了しない
終了時に接続を切るときは`XCloseDisplay()`を使うらしい。これによって全てのウィンドウと確保したリソースが解放される。プログラムは終了しない
行 41: 行 35:
ちょっと逸れて、接続したXサーバから情報を取得してみる。 ちょっと逸れて、接続したXサーバから情報を取得してみる。いろいろマクロがある。
行 43: 行 37:
/* ... */ #include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>

void show(Display *d)
{
    int def_screen = DefaultScreen(d);

    printf("Server Vendor: %s, release %d\n", ServerVendor(d), VendorRelease(d));
    printf("\tProtocol version %d, revision %d\n", ProtocolVersion(d), ProtocolRevision(d));
    printf("Found %d screen\n", ScreenCount(d));
    printf("Number of default screen: %d\n", def_screen);
    printf("Size of default screen: %dx%d\n", DisplayWidth(d, def_screen), DisplayHeight(d, def_screen));
    printf("\tDepth of Root Window: %d\n", DefaultDepth(d, def_screen));
}

int main(void)
{
    Display *d;

    d = XOpenDisplay(NULL);
    if (!d) {
        fprintf(stderr, "failed to connect X server\n");
        exit(1);
    } else {
        show(d);
    }

    XCloseDisplay(d);

    return 0;
}
行 45: 行 71:
試しに手元で実行。
{{{
Server Vendor: The X.Org Foundation, release 10707000
        Protocol version 11, revision 0
Found 1 screen
Number of default screen: 0
Size of default screen: 1920x1080
        Depth of Root Window: 24
}}}

=== ウィンドウの生成 ===
Windowsだとウィンドウクラスを登録して`CreateWindow(Ex)`して…となるが、Xlibの場合`XCreateSimpleWindow()`というのがあった。
{{{
w = XCreateSimpleWindow(
        d, DefaultRootWindow(d), 0, 0, 400, 300, 1,
        BlackPixel(d, 0), WhitePixel(d, 0));
}}}
生成したウィンドウを表示する。
{{{
XMapWindow(d, w);
}}}

=== イベントの処理 ===
まずどんな種類のイベントを受け取るかのマスクを指定する。とりあえず、ウィンドウの破棄(`DestroyNotify`)を受け取りたいので、`StructureNotifyMask`を立てておく。
{{{
XSelectInput(d, w, StructureNotifyMask);
}}}
続いてイベントループに入る。Windowsだと`GetMessage()`やら`PeekMessage()`を使ったお決まりのパターンがあるけれど、Xlibでは`XNextEvent()`というのがあるらしい。イベントループの部分は関数に追い出してみることにする。
{{{#!highlight c
void event_loop(void)
{
    XEvent event;

    for (;;) {
        XNextEvent(d, &event);

        switch (event.type) {
        case DestroyNotify:
            return;
        }
    }
}
}}}
大体揃ったのでこれまでのコードをまとめる。
{{{#!highlight c
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>

void event_loop(void);

Display *d;
Window w;

int main(int argc, char **argv)
{
    d = XOpenDisplay(NULL);
    if (!d) {
        fprintf(stderr, "failed to open display\n");
        exit(1);
    }
    w = XCreateSimpleWindow(
            d, DefaultRootWindow(d), 0, 0, 400, 300, 1,
            BlackPixel(d, 0), WhitePixel(d, 0));
    XMapWindow(d, w);

    XSelectInput(d, w, StructureNotifyMask);
    event_loop();
    XCloseDisplay(d);

    return 0;
}

void event_loop(void)
{
    XEvent event;

    for (;;) {
        XNextEvent(d, &event);

        switch (event.type) {
        case DestroyNotify:
            return;
        }
    }
}
}}}

== ウィンドウマネージャとやりとりする ==
上記のプログラムをコンパイルすると動くことは動くのだが、いくつか問題が発生する。
=== ウィンドウを閉じるとき ===
クライアント側で特に終了動作を規定していない(例えば、クリックされたら終了、とか)ので、ウィンドウマネージャから閉じてやるなり`xkill`を使うなりしてウィンドウを閉じてやる必要がある。しかし、実際にそうすると
{{{
XIO: fatal IO error 35 (Resource temporarily unavailable) on X server ":0.0"
      after 16 requests (16 known processed) with 0 events remaining.
}}}
きゃー。調べてみると、どうやらウィンドウを閉じようとしている際の通知(`WM_DELETE_WINDOW`)はウィンドウマネージャからやってくるらしく、それを受け取らないとこうなるらしい。
 * http://stackoverflow.com/questions/1157364/intercept-wm-delete-window-on-x11
 * http://www.lemoda.net/c/xlib-wmclose/
...

Xクライアントの作成

しばらくXクライアントを作って感覚をつかんでみる。

こんなのがあったのでざっと読んでみると、割とWindowsでAPI叩いてプログラム書くのに似ている(むしろ逆なのだろう)。GC(Graphics Context)とかハンドルとかは大体同じような理解で良さそう。

「何もしない」やつを作ってみる

お約束のウィンドウが出るだけというアレをステップを追いつつ作ってみる。

Xサーバへの接続

何はともあれまずXサーバに接続する。必要なのはXサーバの動いているホストのアドレスとディスプレイ番号。ちなみに最初のディスプレイは0番。この辺は普通に使ってるだけでもわかるか。

XOpenDisplay
Xサーバと接続する

Display *XOpenDisplay(char *display_name);
  • nameに"<address>:<displaynumber>"の形式で指定する。NULLだとDISPLAY環境変数を見に行くらしい

  • Display型のポインタが帰ってくるので、これを使ってXサーバとやりとりする。失敗するとNULLが来る

終了時に接続を切るときはXCloseDisplay()を使うらしい。これによって全てのウィンドウと確保したリソースが解放される。プログラムは終了しない。

とりあえず接続して終了するだけならこんな感じか。

Display *d;
d = XOpenDisplay(NULL);
if (!d) {
    fprintf(stderr, "failed to connect X server\n");
    exit(1);
}

/* ... */

XCloseDisplay(d);

ディスプレイの情報を取得してみる

ちょっと逸れて、接続したXサーバから情報を取得してみる。いろいろマクロがある。

   1 #include <X11/Xlib.h>
   2 #include <X11/Xutil.h>
   3 #include <stdio.h>
   4 #include <stdlib.h>
   5 
   6 void show(Display *d)
   7 {
   8     int def_screen = DefaultScreen(d);
   9 
  10     printf("Server Vendor: %s, release %d\n", ServerVendor(d), VendorRelease(d));
  11     printf("\tProtocol version %d, revision %d\n", ProtocolVersion(d), ProtocolRevision(d));
  12     printf("Found %d screen\n", ScreenCount(d));
  13     printf("Number of default screen: %d\n", def_screen);
  14     printf("Size of default screen: %dx%d\n", DisplayWidth(d, def_screen), DisplayHeight(d, def_screen));
  15     printf("\tDepth of Root Window: %d\n", DefaultDepth(d, def_screen));
  16 }
  17 
  18 int main(void)
  19 {
  20     Display *d;
  21 
  22     d = XOpenDisplay(NULL);
  23     if (!d) {
  24         fprintf(stderr, "failed to connect X server\n");
  25         exit(1);
  26     } else {
  27         show(d);
  28     }
  29 
  30     XCloseDisplay(d);
  31 
  32     return 0;
  33 }

試しに手元で実行。

Server Vendor: The X.Org Foundation, release 10707000
        Protocol version 11, revision 0
Found 1 screen
Number of default screen: 0
Size of default screen: 1920x1080
        Depth of Root Window: 24

ウィンドウの生成

Windowsだとウィンドウクラスを登録してCreateWindow(Ex)して…となるが、Xlibの場合XCreateSimpleWindow()というのがあった。

w = XCreateSimpleWindow(
        d, DefaultRootWindow(d), 0, 0, 400, 300, 1,
        BlackPixel(d, 0), WhitePixel(d, 0));

生成したウィンドウを表示する。

XMapWindow(d, w);

イベントの処理

まずどんな種類のイベントを受け取るかのマスクを指定する。とりあえず、ウィンドウの破棄(DestroyNotify)を受け取りたいので、StructureNotifyMaskを立てておく。

XSelectInput(d, w, StructureNotifyMask);

続いてイベントループに入る。WindowsだとGetMessage()やらPeekMessage()を使ったお決まりのパターンがあるけれど、XlibではXNextEvent()というのがあるらしい。イベントループの部分は関数に追い出してみることにする。

   1 void event_loop(void)
   2 {
   3     XEvent event;
   4 
   5     for (;;) {
   6         XNextEvent(d, &event);
   7 
   8         switch (event.type) {
   9         case DestroyNotify:
  10             return;
  11         }
  12     }
  13 }

大体揃ったのでこれまでのコードをまとめる。

   1 #include <X11/Xlib.h>
   2 #include <X11/Xutil.h>
   3 #include <stdio.h>
   4 #include <stdlib.h>
   5 
   6 void event_loop(void);
   7 
   8 Display *d;
   9 Window w;
  10 
  11 int main(int argc, char **argv)
  12 {
  13     d = XOpenDisplay(NULL);
  14     if (!d) {
  15         fprintf(stderr, "failed to open display\n");
  16         exit(1);
  17     }
  18     w = XCreateSimpleWindow(
  19             d, DefaultRootWindow(d), 0, 0, 400, 300, 1,
  20             BlackPixel(d, 0), WhitePixel(d, 0));
  21     XMapWindow(d, w);
  22 
  23     XSelectInput(d, w, StructureNotifyMask);
  24     event_loop();
  25     XCloseDisplay(d);
  26 
  27     return 0;
  28 }
  29 
  30 void event_loop(void)
  31 {
  32     XEvent event;
  33 
  34     for (;;) {
  35         XNextEvent(d, &event);
  36 
  37         switch (event.type) {
  38         case DestroyNotify:
  39             return;
  40         }
  41     }
  42 }

ウィンドウマネージャとやりとりする

上記のプログラムをコンパイルすると動くことは動くのだが、いくつか問題が発生する。

ウィンドウを閉じるとき

クライアント側で特に終了動作を規定していない(例えば、クリックされたら終了、とか)ので、ウィンドウマネージャから閉じてやるなりxkillを使うなりしてウィンドウを閉じてやる必要がある。しかし、実際にそうすると

XIO:  fatal IO error 35 (Resource temporarily unavailable) on X server ":0.0"
      after 16 requests (16 known processed) with 0 events remaining.

きゃー。調べてみると、どうやらウィンドウを閉じようとしている際の通知(WM_DELETE_WINDOW)はウィンドウマネージャからやってくるらしく、それを受け取らないとこうなるらしい。

...

clear/wm_devel/2011-08-16 (最終更新日時 2011-11-06 23:19:53 更新者 clear)