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 <stdio.h> 17 #include <stdlib.h> 18 #include <X11/Xlib.h> 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 <time.h> 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