ex3.c (6666B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <unistd.h> 5 #include <string.h> 6 #include <math.h> 7 8 #include <X11/Xlib.h> 9 10 /* macros */ 11 #define INIT_WIDTH 800 12 #define INIT_HEIGHT 600 13 #define FPS 60 14 15 /* 16 #define COUNT_FPS 17 */ 18 19 enum keys { 20 KEY_D, 21 KEY_S, 22 KEY_A, 23 KEY_W, 24 KEY_Q, 25 KEY_SPACE, 26 NUM_KEY, //number of keys in this enum 27 }; 28 enum key_state { 29 KEY_UP, 30 KEY_DOWN, 31 }; 32 enum next_menu { 33 START_MENU, 34 GAME_PLAY, 35 GAME_OVER, 36 QUIT, 37 }; 38 39 /* variables */ 40 Display *display; 41 Window window; 42 unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT; 43 GC gc; 44 Atom wm_delete_window; 45 float px = 200, py = 200; 46 float vx = 0, vy = 0; 47 int width = 40, height = 40; 48 int next_menu = START_MENU; 49 50 51 /* function prototypes */ 52 void setup(void); 53 void cleanup(void); 54 void start_menu(void); 55 void game_play(void); 56 void receive_events(int[]); 57 void handle_inputs(int[]); 58 void next_tick(long); 59 void game_over(void); 60 61 62 void 63 setup(void) 64 { 65 if ((display = XOpenDisplay(NULL)) == NULL){ 66 fprintf(stderr, "ERROR: could not open display\n"); 67 exit(1); 68 } 69 window = XCreateSimpleWindow( 70 display, 71 XDefaultRootWindow(display), 72 0, 0, 73 win_width, win_height, 74 0, 0, 75 0); 76 XStoreName(display, window, "UNKO"); 77 gc = XCreateGC(display, window, 0, NULL); 78 79 wm_delete_window = XInternAtom(display, 80 "WM_DELETE_WINDOW", False); 81 XSetWMProtocols(display, window, &wm_delete_window, 1); 82 83 XSelectInput(display, window, 84 ExposureMask | KeyPressMask | KeyReleaseMask); 85 86 XSetForeground(display, gc, 0x00FFFF); 87 XMapWindow(display, window); 88 } 89 90 void 91 start_menu(void) 92 { 93 XEvent event; 94 char *menu_char_q = "press q to quit."; 95 char *menu_char_s = "press <space> to start."; 96 97 XClearArea(display, window, 98 0, 0, // position 99 win_width, win_height, // width and height 100 False); 101 XDrawString(display, window, gc, 102 win_width/2 - strlen(menu_char_q)/2, win_height/2, 103 menu_char_q, strlen(menu_char_q)); 104 XDrawString(display, window, gc, 105 win_width/2 - strlen(menu_char_s)/2, win_height/2 + 20, 106 menu_char_s, strlen(menu_char_s)); 107 108 while (next_menu == START_MENU) { 109 XNextEvent(display, &event); 110 switch (event.type) { 111 case Expose: { 112 XDrawString(display, window, gc, 113 win_width/2 - strlen(menu_char_q)/2, 114 win_height/2, 115 menu_char_q, strlen(menu_char_q)); 116 XDrawString(display, window, gc, 117 win_width/2 - strlen(menu_char_s)/2, 118 win_height/2 + 20, 119 menu_char_s, strlen(menu_char_s)); 120 121 } break; 122 case KeyPress: { 123 switch (XLookupKeysym(&event.xkey, 0)) { 124 case 'q': 125 next_menu = QUIT; 126 break; 127 case ' ': 128 next_menu = GAME_PLAY; 129 break; 130 default: 131 break; 132 } 133 } break; 134 case ClientMessage: { 135 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 136 next_menu = QUIT; 137 } 138 } break; 139 default: 140 break; 141 } 142 } 143 } 144 145 void 146 receive_events(int key_state[]) 147 { 148 XEvent event; 149 XWindowAttributes wattr; 150 151 while (XPending(display) > 0) { 152 XNextEvent(display, &event); 153 switch (event.type) { 154 case Expose: { 155 XGetWindowAttributes(display, window, &wattr); 156 win_width = wattr.width; 157 win_height = wattr.height; 158 } break; 159 case KeyPress: { 160 switch (XLookupKeysym(&event.xkey, 0)) { 161 case 'q': 162 //next_menu = GAME_OVER; 163 key_state[KEY_Q] = KEY_DOWN; 164 break; 165 case 'd': 166 key_state[KEY_D] = KEY_DOWN; 167 break; 168 case 'a': 169 key_state[KEY_A] = KEY_DOWN; 170 break; 171 case 'w': 172 key_state[KEY_W] = KEY_DOWN; 173 break; 174 case 's': 175 key_state[KEY_S] = KEY_DOWN; 176 break; 177 default: 178 break; 179 } 180 } break; 181 case KeyRelease: { 182 switch (XLookupKeysym(&event.xkey, 0)) { 183 case 'q': 184 key_state[KEY_Q] = KEY_UP; 185 break; 186 case 'd': 187 key_state[KEY_D] = KEY_UP; 188 break; 189 case 'a': 190 key_state[KEY_A] = KEY_UP; 191 break; 192 case 'w': 193 key_state[KEY_W] = KEY_UP; 194 break; 195 case 's': 196 key_state[KEY_S] = KEY_UP; 197 break; 198 default: 199 break; 200 } 201 } break; 202 case ClientMessage: { 203 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 204 next_menu = QUIT; 205 } 206 } break; 207 default: 208 break; 209 } 210 } 211 } 212 213 void 214 handle_inputs(int key_state[]) 215 { 216 if (key_state[KEY_Q] == KEY_DOWN){ 217 next_menu = GAME_OVER; 218 return; 219 } 220 vx = vy = 0; 221 if (key_state[KEY_D] == KEY_DOWN) 222 vx += 300; 223 if (key_state[KEY_A] == KEY_DOWN) 224 vx += -300; 225 if (key_state[KEY_S] == KEY_DOWN) 226 vy += 300; 227 if (key_state[KEY_W] == KEY_DOWN) 228 vy += -300; 229 } 230 231 void 232 next_tick(long ndt) // nano second 233 { 234 px = px + vx * ndt / 1000 / 1000 / 1000; 235 py = py + vy * ndt / 1000 / 1000 / 1000; 236 // bind within the window 237 if (px < 0) 238 px = 0; 239 if (win_width < px + width) 240 px = win_width - width; 241 if (py < 0) 242 py = 0; 243 if (win_height < py + height) 244 py = win_height - height; 245 } 246 247 void 248 game_play(void) 249 { 250 int key_state[NUM_KEY]; 251 long t0, t1, dt; 252 #ifdef COUNT_FPS 253 int fps_count = 0; 254 #endif 255 struct timespec ts; 256 257 while (next_menu == GAME_PLAY){ 258 clock_gettime(CLOCK_MONOTONIC, &ts); 259 t0 = ts.tv_nsec; 260 receive_events(key_state); 261 handle_inputs(key_state); 262 263 // fix fps 264 // TODO: This method create some strange stripe when 265 // rendered in 60fps on a 60fps monitor. 266 dt = 0; 267 while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){ 268 clock_gettime(CLOCK_MONOTONIC, &ts); 269 t1 = ts.tv_nsec; 270 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000; 271 } 272 #ifdef COUNT_FPS 273 // count fps. 274 fps_count++; 275 if (t1 < t0){ 276 printf("fps: %u\n", fps_count); 277 fps_count = 0; 278 } 279 #endif 280 281 clock_gettime(CLOCK_MONOTONIC, &ts); 282 t0 = ts.tv_nsec; 283 284 next_tick(1000 * 1000 * 1000 / FPS); 285 286 XClearArea(display, window, 287 0, 0, // position 288 win_width, win_height, // width and height 289 False); 290 XFillRectangle(display, window, gc, 291 px, py, // position 292 width, height); // width and height 293 } 294 } 295 296 void 297 game_over(void) 298 { 299 XEvent event; 300 char *menu_char = "GAME OVER"; 301 302 XClearArea(display, window, 303 0, 0, // position 304 win_width, win_height, // width and height 305 False); 306 XDrawString(display, window, gc, 307 win_width/2 - strlen(menu_char)/2, win_height/2, 308 menu_char, strlen(menu_char)); 309 XFlush(display); 310 311 sleep(1); 312 next_menu = START_MENU; 313 } 314 315 void 316 cleanup(void) 317 { 318 XCloseDisplay(display); 319 } 320 321 int 322 main(void) 323 { 324 setup(); 325 while (next_menu != QUIT){ 326 switch (next_menu){ 327 case START_MENU: 328 start_menu(); 329 break; 330 case GAME_PLAY: 331 game_play(); 332 break; 333 case GAME_OVER: 334 game_over(); 335 break; 336 default: 337 break; 338 } 339 } 340 341 cleanup(); 342 return 0; 343 }