xlib_playground

Xlib playground for experiments.
Log | Files | Refs

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 }