www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

xlib2.html (5814B)


      1 +++
      2 date = '2022-12-16T00:00:00+09:00'
      3 draft = true
      4 title = 'Xlibで遊んでみる'
      5 +++
      6 <time>2022-12-16</time>
      7 
      8 <h2>はじめに</h2>
      9 <p>X11でGUIのプログラミングをしてみようと思い、してみた。X11用の低レベルのライブラリはXlibとxcbの二つがあるようだ。x.orgのウェブページを見てみると、Xlibは古く、xcbに置きかわりつつあるという。そのため、新しくなにかを作る場合はxcbを使うようにとのことである。ところがこのxcbはドキュメンテーションに乏しく、X11を触るのが初めての人間にはなにをどうすればいいのかほとんど分からなかった。知らない関数や構造体やらがでてきても(殆ど全部知らないものだが)、その関数なり構造体なりの説明がどこにも見当たらない。manページもない。あるのはdoxygenなるものでソースコードのコメントから自動生成したいい加減なものだけで、使いものにならない。</p>
     10 <p>とりあえずX11のことを少しは理解してからでないと初められそうもないと思い、もう少しましな情報があるXlibから始めることにした。</p>
     11 <p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
     12 
     13 <h2>初期設定</h2>
     14 <p>ディスプレイを開き、ウィンドウを作成する。変数はとりあえずグローバルに宣言することにした。<code>main</code>関数はできるだけ小さくして実際の処理はそれぞれの関数にさせてみる:</p>
     15 <pre><code>
     16 #include &lt;stdio.h&gt;
     17 #include &lt;stdlib.h&gt;
     18 #include &lt;X11/Xlib.h&gt;
     19 
     20 /* macros */
     21 #define INIT_WIDTH 800
     22 #define INIT_HEIGHT 600
     23 
     24 /* variables */
     25 Display     *display;
     26 Window       window;
     27 unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT;
     28 GC           gc;
     29 Atom         wm_delete_window;
     30 
     31 void
     32 setup(void)
     33 {
     34 	// Open a display.
     35 	if ((display = XOpenDisplay(NULL)) == NULL){
     36 	    fprintf(stderr, "ERROR: could not open display\n");
     37 	    exit(1);
     38 	}
     39 	// Create a window.
     40 	window = XCreateSimpleWindow(
     41 	                    display,
     42 	                    XDefaultRootWindow(display),
     43 	                    0, 0,
     44 	                    win_width, win_height,
     45 	                    0, 0,
     46 	                    0);
     47 	XStoreName(display, window, "UNKO");
     48 
     49 	// Setup a graphical context.
     50 	gc = XCreateGC(display, window, 0, NULL);
     51 	XSetForeground(display, gc, 0xFFFFFF);
     52 
     53 	// Kill the application when the window is destroyed.	
     54 	wm_delete_window = XInternAtom(display,
     55 	                               "WM_DELETE_WINDOW", False);
     56 	XSetWMProtocols(display, window, &wm_delete_window, 1);
     57 
     58 	// Setup which input to process.
     59 	XSelectInput(display, window,
     60 	             ExposureMask|KeyPressMask|KeyReleaseMask);
     61 
     62 	// Actually draw the window.
     63 	XMapWindow(display, window);
     64 }
     65 
     66 void
     67 clean_up(void)
     68 {
     69 	XCloseDisplay(display);
     70 }
     71 </code></pre>
     72 
     73 <p>適当な四角形のものを表示し、その位置を時間の関数として動かしてみる。</p>
     74 <pre><code>#include &lt;time.h&gt;
     75 
     76 int
     77 main(void)
     78 {
     79     int px, py;
     80     int quit;
     81     struct timespec ts;
     82     XEvent event;
     83 
     84     setup();
     85     quit = 0;
     86 
     87     while (!quit){
     88         while(XPending(display) > 0){
     89             XNextEvent(display, &event);
     90             switch (event.type){
     91             case KeyPress: {
     92                 switch (XLookupKeysym(&event.xkey, 0)){
     93                 case 'q':
     94                     quit = 1;
     95                     break;
     96                 default:
     97                     break;
     98                 }
     99             } break;
    100             case ClientMessage: {
    101                 if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
    102                     quit = 1;
    103                 }
    104             } break;
    105             default:
    106                 break;
    107             }
    108         }
    109         clock_gettime(CLOCK_MONOTONIC, &ts);
    110         px = 200 + (int) (100 * sinf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
    111         py = 200 + (int) (100 * cosf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
    112         XClearArea(display, window,
    113                    0, 0,                  // position
    114                    win_width, win_height, // width and height
    115                    False);
    116         XFillRectangle(display, window, gc,
    117                        px, py,    // position
    118                        100, 100);   // width and height
    119 
    120         ts.tv_sec = 0;
    121         ts.tv_nsec = 10 * 1000 * 1000;
    122         nanosleep(&ts, NULL);
    123 	}
    124 
    125 	cleanup();
    126 	return 0;
    127 }
    128 </code></pre>
    129 
    130 <p>文字を表示。<code>XDrawString</code></p>
    131 
    132 <h2>キーボード入力</h2>
    133 <p>キーボードの入力を受け付けるようにする。入力によって四角形の位置や速度を変化させる。</p>
    134 <p>ここまでできればかなりなんでもできるようになる。</p>
    135 
    136 <h2>FPSの固定</h2>
    137 <p>前のフレームからの経過時間を計測して1/FPSを越えるまで待機させる。このときに<code>nanosleep</code>を使うとなぜか上手くいかなかった。OSに処理をブロックされる為か?とりあえず<code>while</code>ループの中でひたすら時刻を読んでいるのだが、これでいいんかな。リソースの無駄遣いではないんかな。</p>
    138 
    139 <h2>メニューの実装</h2>
    140 <p>メニューとゲームの推移を実装した。</p>
    141 
    142 <h2>当たり判定</h2>
    143 <p>とりあえず適当に実装した。2体間の衝突のみ判定しているので近くに別の物体がいるとめりこむ場合がある。</p>
    144 </p>
    145 
    146 
    147 <h2>参考</h2>
    148 <ul>
    149 <li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
    150 <li><a href="https://www.youtube.com/watch?v=764fnfEb1_c">X11 App in C with Xlib(youtube video by tsoding)</a></li>
    151 </ul>
    152