xlib_playground2.html (7147B)
1 +++ 2 date = '2022-12-22T00:00:00+09:00' 3 draft = false 4 title = 'Xlibで遊んでみる2' 5 +++ 6 <time>2022-12-22</time> 7 8 <p>前回: <a href="xlib_playground1.html">Xlibで遊んでみる1</a></p> 9 <p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。 10 </p> 11 12 <h2>FPSの固定</h2> 13 <p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな? 14 </p> 15 16 <p> 17 とりあえず<code>while</code>ループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか: 18 </p> 19 <pre><code>#defin FPS 60 20 21 int 22 main(void) 23 { 24 long t0, t1, dt; 25 int fps_count; 26 27 clock_gettime(CLOCK_MONOTONIC, &ts); 28 t0 = ts.tv_nsec; 29 30 while (!quit) { 31 // fix fps 32 dt = 0; 33 while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){ 34 clock_gettime(CLOCK_MONOTONIC, &ts); 35 t1 = ts.tv_nsec; 36 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000; 37 } 38 // count fps. 39 fps_count++; 40 if (t1 < t0){ 41 printf("fps: %u\n", fps_count); 42 fps_count = 0; 43 } 44 clock_gettime(CLOCK_MONOTONIC, &ts); 45 t0 = ts.tv_nsec; 46 } 47 } 48 </code></pre> 49 <p> 50 時刻は<code>clock_gettime()</code>で測定して1秒未満の部分: <code>tv_nsec</code>だけを利用している。<code>tv_nsec</code>はナノ秒ナノで、10<sup>9</sup>を掛けている。<code>dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000</code>で前回の時刻と現在の時刻の少数部分を比較している。繰り上がりがあれば前回の時刻よりも現在の時刻の方が小さくなるので1秒足すことで調整している。</p> 51 <p> 52 FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。</p> 53 <p> 54 あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。</p> 55 56 <h2>キーボード入力の処理</h2> 57 <p>キーボードからの入力を受け取る:</p> 58 <pre><code>XSelectInput(display, window, 59 ExposureMask|KeyPressMask|KeyReleaseMask); 60 </code></pre> 61 <p>ここではキーボードのキーを押した時と離した時に<code>XEvent</code>の通知を受け取るように設定した。 62 </p> 63 <p> 64 <code>XNextEvent()</code>からひとつずつ入力を受け取ると、複数のキーが同時に押された時にうまく処理できなかったので、押されているキーを配列に保存しておくことにした:</p> 65 <pre><code>enum Keys { 66 Key_D, 67 Key_S, 68 Key_A, 69 Key_W, 70 Key_Space, 71 Num_Key, //number of keys in this enum 72 }; 73 enum Key_State { 74 Key_Up, 75 Key_Down, 76 }; 77 78 int key_state[Num_Key]; 79 </code></pre> 80 81 <p> 82 入力の処理は<code>handle_inputs()</code>関数内で行なう。<code>A</code>、<code>S</code>、<code>D</code>、<code>W</code>のうちどれかのキーが押されているとそれぞれ左、下、右、上方向に速度を加算するようにした。また、<code>Q</code>が押されるか、windowが破壊されると<code>quit</code>フラグを<code>1</code>にしてメインループから抜けるようにしている:</p> 83 <pre><code>int quit; 84 85 void 86 handle_inputs(void) 87 { 88 XEvent event; 89 while (XPending(display) > 0) { 90 XNextEvent(display, &event); 91 switch (event.type) { 92 case KeyPress: { 93 switch (XLookupKeysym(&event.xkey, 0)) { 94 case 'q': 95 quit = 1; 96 break; 97 case 'd': 98 key_state[Key_D] = Key_Down; 99 break; 100 case 'a': 101 key_state[Key_A] = Key_Down; 102 break; 103 case 'w': 104 key_state[Key_W] = Key_Down; 105 break; 106 case 's': 107 key_state[Key_S] = Key_Down; 108 break; 109 default: 110 break; 111 } 112 } break; 113 case KeyRelease: { 114 switch (XLookupKeysym(&event.xkey, 0)) { 115 case 'd': 116 key_state[Key_D] = Key_Up; 117 break; 118 case 'a': 119 key_state[Key_A] = Key_Up; 120 break; 121 case 'w': 122 key_state[Key_W] = Key_Up; 123 break; 124 125 case 's': 126 key_state[Key_S] = Key_Up; 127 break; 128 default: 129 break; 130 } 131 } break; 132 case ClientMessage: { 133 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 134 quit = 1; 135 } 136 } break; 137 default: 138 break; 139 } 140 } 141 142 vx = vy = 0; 143 if (key_state[Key_D] == Key_Down) 144 vx += 300; 145 if (key_state[Key_A] == Key_Down) 146 vx += -300; 147 if (key_state[Key_S] == Key_Down) 148 vy += 300; 149 if (key_state[Key_W] == Key_Down) 150 vy += -300; 151 } 152 </code></pre> 153 154 <p> 155 入力によって変更された速度は、<code>main()</code>関数内で次の座標を計算するために使用される: 156 </p> 157 <pre><code>float px = 200, py = 200; 158 float vx = 0, vy = 0; 159 int width = 40, height = 40; 160 161 int 162 main(void) 163 { 164 /* ... */ 165 quit = 0; 166 while (!quit) { 167 handle_input() 168 /* ... */ 169 px = px + vx * dt / 1000 / 1000 / 1000; 170 py = py + vy * dt / 1000 / 1000 / 1000; 171 // bind within the window 172 if (px < 0) 173 px = 0; 174 if (win_width < px + width) 175 px = win_width - width; 176 if (py < 0) 177 py = 0; 178 if (win_height < py + height) 179 py = win_height - height; 180 181 XClearArea(display, window, 182 0, 0, // position 183 win_width, win_height, // width and height 184 False); 185 XFillRectangle(display, window, gc, 186 px, py, // position 187 width, height); // width and height 188 } 189 /* ... */ 190 } 191 </code></pre> 192 193 <h2>完成品</h2> 194 <a href="https://git.mtkn.jp/xlib_playground/file/ex2/ex2.c.html">ソースコード</a> 195 <p>色を変えてみた。</p> 196 <video controls> 197 <source src="videos/ex2.webm" type="video/webm"> 198 </video> 199 200 <h2>参考</h2> 201 <ul> 202 <li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li> 203 </ul> 204 <p>次の記事: <a href="xlib_playground3.html">Xlibで遊んでみる3</a> 205 </p> 206