xlib_playground

Xlib playground for experiments.
Log | Files | Refs

ex5.c (13324B)


      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 (250)
     12 #define INIT_HEIGHT (200)
     13 #define FPS (60)
     14 #define SUB_TICK (4)
     15 #define NUM_CIRCLE (50)
     16 #define max(a, b) ((a) > (b) ? (a) : (b))
     17 #define min(a, b) ((a) < (b) ? (a) : (b))
     18 
     19 // #define COUNT_FPS
     20 
     21 enum keys {
     22 	KEY_D,
     23 	KEY_S,
     24 	KEY_A,
     25 	KEY_W,
     26 	KEY_Q,
     27 	KEY_SPACE,
     28 	NUM_KEY, //number of keys in this enum
     29 };
     30 enum key_state {
     31 	KEY_UP,
     32 	KEY_DOWN,
     33 };
     34 enum next_menu {
     35 	START_MENU,
     36 	GAME_PLAY,
     37 	GAME_OVER,
     38 	QUIT,
     39 };
     40 
     41 struct rect {
     42 	float ppx, ppy; // previous position
     43 	float px, py;   // top left corner
     44 	float vx, vy;
     45 	int w, h;
     46 	int m;
     47 };
     48 
     49 struct circle {
     50 	float ppx, ppy;
     51 	float px, py;
     52 	float vx, vy;
     53 	int r;
     54 	int m;
     55 };
     56 
     57 /* variables */
     58 Display      *display;
     59 Window        window;
     60 unsigned int  win_width = INIT_WIDTH, win_height = INIT_HEIGHT;
     61 GC            gc, sgc[NUM_CIRCLE];
     62 Atom          wm_delete_window;
     63 struct circle circle[NUM_CIRCLE];
     64 int           next_menu = START_MENU;
     65 
     66 
     67 /* function prototypes */
     68 void setup(void);
     69 void cleanup(void);
     70 void start_menu(void);
     71 void game_play(void);
     72 void receive_events(int[]);
     73 void handle_inputs(int[]);
     74 void rect_next_tick(struct rect *, long);
     75 int  rect_test_collision(struct rect *, struct rect *);
     76 void rect_handle_collision_mf(struct rect *, struct rect *);
     77 void rect_handle_collision(struct rect *, struct rect *);
     78 void rect_handle_collision_elastic(struct rect *, struct rect *);
     79 void circle_next_tick(struct circle *, long);
     80 int  circle_test_collision(struct circle *, struct circle *);
     81 void game_over(void);
     82 
     83 
     84 void
     85 setup(void)
     86 {
     87 	if ((display = XOpenDisplay(NULL)) == NULL){
     88 	    fprintf(stderr, "ERROR: could not open display\n");
     89 	    exit(1);
     90 	}
     91 	window = XCreateSimpleWindow(
     92 	                    display,
     93 	                    XDefaultRootWindow(display),
     94 	                    0, 0,
     95 	                    win_width, win_height,
     96 	                    0, 0,
     97 						0);
     98 	XStoreName(display, window, "UNKO");
     99 	gc = XCreateGC(display, window, 0, NULL);
    100 	XSetForeground(display, gc, 0x00FFFF);
    101 	for (int i = 0; i < NUM_CIRCLE; i++){
    102 		sgc[i] = XCreateGC(display, window, 0, NULL);
    103 		XSetForeground(display, sgc[i], 0x00FF00);
    104 	}
    105 
    106 	wm_delete_window = XInternAtom(display,
    107 	                               "WM_DELETE_WINDOW", False);
    108 	XSetWMProtocols(display, window, &wm_delete_window, 1);
    109 
    110 	XSelectInput(display, window,
    111 	             ExposureMask | KeyPressMask | KeyReleaseMask);
    112 
    113 	XMapWindow(display, window);
    114 }
    115 
    116 void
    117 start_menu(void)
    118 {
    119 	XEvent event;
    120 	char *menu_char_q = "press q to quit.";
    121 	char *menu_char_s = "press <space> to start.";
    122 
    123 	XClearArea(display, window,
    124 			   0, 0,                  // position
    125 			   win_width, win_height, // width and height
    126 			   False);
    127 	XDrawString(display, window, gc,
    128 				win_width/2 - strlen(menu_char_q)/2, win_height/2,
    129 				menu_char_q, strlen(menu_char_q));
    130 	XDrawString(display, window, gc,
    131 				win_width/2 - strlen(menu_char_s)/2, win_height/2 + 20,
    132 				menu_char_s, strlen(menu_char_s));
    133 
    134 	while (next_menu == START_MENU) {
    135 		XNextEvent(display, &event);
    136 		switch (event.type) {
    137 		case Expose: {
    138 			XDrawString(display, window, gc,
    139 						win_width/2 - strlen(menu_char_q)/2,
    140 						win_height/2,
    141 						menu_char_q, strlen(menu_char_q));
    142 			XDrawString(display, window, gc,
    143 						win_width/2 - strlen(menu_char_s)/2,
    144 						win_height/2 + 20,
    145 						menu_char_s, strlen(menu_char_s));
    146 
    147 		} break;
    148 		case KeyPress: {
    149 			switch (XLookupKeysym(&event.xkey, 0)) {
    150 			case 'q':
    151 				next_menu = QUIT;
    152 				break;
    153 			case ' ':
    154 				next_menu = GAME_PLAY;
    155 				break;
    156 			default:
    157 				break;
    158 			}
    159 		} break;
    160 		case ClientMessage: {
    161 			if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
    162 				next_menu = QUIT;
    163 			}
    164 		} break;
    165 		default:
    166 			break;
    167 		}
    168 	}
    169 }
    170 
    171 void
    172 receive_events(int key_state[])
    173 {
    174 	XEvent event;
    175 	XWindowAttributes wattr;
    176 
    177 	while (XPending(display) > 0) {
    178 		XNextEvent(display, &event);
    179 		switch (event.type) {
    180 		case Expose: {
    181 			XGetWindowAttributes(display, window, &wattr);
    182 			win_width = wattr.width;
    183 			win_height = wattr.height;
    184 		} break;
    185 		case KeyPress: {
    186 			switch (XLookupKeysym(&event.xkey, 0)) {
    187 			case 'q':
    188 				//next_menu = GAME_OVER;
    189 				key_state[KEY_Q] = KEY_DOWN;
    190 				break;
    191 			case 'd':
    192 				key_state[KEY_D] = KEY_DOWN;
    193 				break;
    194 			case 'a':
    195 				key_state[KEY_A] = KEY_DOWN;
    196 				break;
    197 			case 'w':
    198 				key_state[KEY_W] = KEY_DOWN;
    199 				break;
    200 			case 's':
    201 				key_state[KEY_S] = KEY_DOWN;
    202 				break;
    203 			default:
    204 				break;
    205 			}
    206 		} break;
    207 		case KeyRelease: {
    208 			switch (XLookupKeysym(&event.xkey, 0)) {
    209 			case 'q':
    210 				key_state[KEY_Q] = KEY_UP;
    211 				break;
    212 			case 'd':
    213 				key_state[KEY_D] = KEY_UP;
    214 				break;
    215 			case 'a':
    216 				key_state[KEY_A] = KEY_UP;
    217 				break;
    218 			case 'w':
    219 				key_state[KEY_W] = KEY_UP;
    220 				break;
    221 			case 's':
    222 				key_state[KEY_S] = KEY_UP;
    223 				break;
    224 			default:
    225 				break;
    226 			}
    227 		} break;
    228 		case ClientMessage: {
    229 			if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
    230 				next_menu = QUIT;
    231 			}
    232 		} break;
    233 		default:
    234 			break;
    235 		}
    236 	}
    237 }
    238 
    239 void
    240 handle_inputs(int key_state[])
    241 {
    242 	if (key_state[KEY_Q] == KEY_DOWN){
    243 		next_menu = GAME_OVER;
    244 		return;
    245 	}
    246 	/*
    247 	circle[0].vx = circle[0].vy = 0;
    248 	if (key_state[KEY_D] == KEY_DOWN)
    249 		circle[0].vx += 300;
    250 	if (key_state[KEY_A] == KEY_DOWN)
    251 		circle[0].vx += -300;
    252 	if (key_state[KEY_S] == KEY_DOWN)
    253 		circle[0].vy += 300;
    254 	if (key_state[KEY_W] == KEY_DOWN)
    255 		circle[0].vy += -300;
    256 	*/
    257 }
    258 
    259 void
    260 rect_next_tick(struct rect *s, long ndt) // nano second
    261 {
    262 	s->ppx = s->px;
    263 	s->ppy = s->py;
    264 	s->px = s->px + s->vx * ndt / 1000 / 1000 / 1000;
    265 	s->py = s->py + s->vy * ndt / 1000 / 1000 / 1000;
    266 
    267 	// bind within the window
    268 	if (s->px < 0) {
    269 		s->px = 0;
    270 		s->vx *= -1;
    271 	}
    272 	if (win_width < s->px + s->w) {
    273 		s->px = win_width - s->w;
    274 		s->vx *= -1;
    275 	}
    276 	if (s->py < 0) {
    277 		s->py = 0;
    278 		s->vy *= -1;
    279 	}
    280 	if (win_height < s->py + s->h) {
    281 		s->py = win_height - s->h;
    282 		s->vy *= -1;
    283 	}
    284 }
    285 
    286 void
    287 circle_next_tick(struct circle *c, long ndt)
    288 {
    289 	c->ppx = c->px;
    290 	c->ppy = c->py;
    291 	c->px = c->px + c->vx * ndt / 1000 / 1000 / 1000;
    292 	c->py = c->py + c->vy * ndt / 1000 / 1000 / 1000;
    293 
    294 	// bind within the window
    295 	if (c->px - c->r < 0) {
    296 		c->px = c->r;
    297 		c->vx *= -1;
    298 	}
    299 	if (win_width < c->px + c->r) {
    300 		c->px = win_width - c->r;
    301 		c->vx *= -1;
    302 	}
    303 	if (c->py - c->r < 0) {
    304 		c->py = c->r;
    305 		c->vy *= -1;
    306 	}
    307 	if (win_height < c->py + c->r) {
    308 		c->py = win_height - c->r;
    309 		c->vy *= -1;
    310 	}
    311 }
    312 
    313 int
    314 rect_test_collision(struct rect *s1, struct rect *s2)
    315 {
    316 	return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w &&
    317 	       s2->py < s1->py + s1->h && s1->py < s2->py + s2->h;
    318 }
    319 
    320 int
    321 circle_test_collision(struct circle *c1, struct circle *c2)
    322 {
    323 	return (c1->px - c2->px) * (c1->px - c2->px) +
    324 	       (c1->py - c2->py) * (c1->py - c2->py) <
    325 		   (c1->r + c2->r) * (c1->r + c2->r);
    326 }
    327 
    328 /*
    329  * Handle collision of a moving rect against fixed rect
    330  */
    331 void
    332 rect_handle_collision_mf(struct rect *sm, struct rect *sf)
    333 {
    334 	if (!rect_test_collision(sm, sf))
    335 		return;
    336 	if (sm->ppx + sm->w <= sf->ppx && sf->px < sm->px + sm->w)
    337 		// collisioin from left to right
    338 		sm->px = sf->px - sm->w;
    339 	if (sf->ppx + sf->w <= sm->ppx && sm->px < sf->px + sf->w)
    340 		// collision from right to left
    341 		sm->px = sf->px + sf->w;
    342 
    343 	if (sm->ppy + sm->h <= sf->ppy && sf->py < sm->py + sm->h)
    344 		// collision from up to down
    345 		sm->py = sf->py - sm->h;
    346 	if (sf->ppy + sf->h <= sm->ppy && sm->py < sf->py + sf->h)
    347 		// collision from dohn to up
    348 		sm->py = sf->py + sf->h;
    349 }
    350 
    351 /*
    352  * Handle collision of a moving rect against another moving rect
    353  */
    354 void
    355 rect_handle_collision_mm(struct rect *s1, struct rect *s2)
    356 {
    357 	if (!rect_test_collision(s1, s2))
    358 		return;
    359 
    360 	float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
    361 	float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
    362 
    363 	if (lapx < lapy) {
    364 		if (s1->px + s1->w < s2->px + s2->w / 2) {
    365 			s1->px -= lapx / 2;
    366 			s2->px += lapx / 2;
    367 		} else {
    368 			s1->px += lapx / 2;
    369 			s2->px -= lapx / 2;
    370 		}
    371 	} else {
    372 		if (s1->py + s1->h < s2->py + s2->h / 2) {
    373 			s1->py -= lapy / 2;
    374 			s2->py += lapy / 2;
    375 		} else {
    376 			s1->py += lapy / 2;
    377 			s2->py -= lapy / 2;
    378 		}
    379 	}
    380 }
    381 
    382 void
    383 circle_handle_collision_mm(struct circle *c1, struct circle *c2)
    384 {
    385 	if (!circle_test_collision(c1, c2))
    386 		return;
    387 
    388 	float col_px = c2->px - c1->px;
    389 	float col_py = c2->py - c1->py;
    390 	float col_pr = sqrtf(col_px * col_px + col_py * col_py);
    391 	col_px /= col_pr;
    392 	col_py /= col_pr;
    393 
    394 	c1->px = c1->px - col_px  / 2;
    395 	c1->py = c1->py - col_py  / 2;
    396 	c2->px = c2->px + col_px  / 2;
    397 	c2->py = c2->py + col_py  / 2;
    398 }
    399 
    400 void
    401 rect_handle_collision_elastic(struct rect *s1, struct rect *s2)
    402 {
    403 	if(!rect_test_collision(s1, s2))
    404 		return;
    405 
    406 	float v1, v2;
    407 	float m1 = s1->m;
    408 	float m2 = s2->m;
    409 
    410 	float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
    411 	float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
    412 
    413 	if (lapx < lapy) {
    414 		v1 = s1->vx;
    415 		v2 = s2->vx;
    416 		s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
    417 		s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
    418 	} else {
    419 		v1 = s1->vy;
    420 		v2 = s2->vy;
    421 		s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
    422 		s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
    423 	}
    424 
    425 	rect_handle_collision_mm(s1, s2);
    426 }
    427 
    428 void
    429 circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
    430 {
    431 	if(!circle_test_collision(c1, c2))
    432 		return;
    433 
    434 	float col_px = c2->px - c1->px;
    435 	float col_py = c2->py - c1->py;
    436 	float col_pr = sqrtf(col_px * col_px + col_py * col_py);
    437 	col_px /= col_pr;
    438 	col_py /= col_pr;
    439 	float nor_px = col_py;
    440 	float nor_py = -col_px;
    441 
    442 	float m1 = c1->m;
    443 	float m2 = c2->m;
    444 
    445 	float col_1v = c1->vx * col_px + c1->vy * col_py;
    446 	float col_2v = c2->vx * col_px + c2->vy * col_py;
    447 
    448 	float col_1vxn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_px;
    449 	float col_1vyn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_py;
    450 	float col_2vxn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_px;
    451 	float col_2vyn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_py;
    452 
    453 	float nor_1vx = nor_px * (c1->vx * nor_px + c1->vy * nor_py);
    454 	float nor_1vy = nor_py * (c1->vx * nor_px + c1->vy * nor_py);
    455 	float nor_2vx = nor_px * (c2->vx * nor_px + c2->vy * nor_py);
    456 	float nor_2vy = nor_py * (c2->vx * nor_px + c2->vy * nor_py);
    457 
    458 	c1->vx = col_1vxn + nor_1vx;
    459 	c1->vy = col_1vyn + nor_1vy;
    460 	c2->vx = col_2vxn + nor_2vx;
    461 	c2->vy = col_2vyn + nor_2vy;
    462 
    463 	circle_handle_collision_mm(c1, c2);
    464 }
    465 void
    466 game_play(void)
    467 {
    468 	int  key_state[NUM_KEY];
    469 	long t0, t1, dt;
    470 #ifdef COUNT_FPS
    471 	int fps_count = 0;
    472 #endif
    473 	struct timespec ts;
    474 
    475 	for(int i = 0; i < NUM_CIRCLE; i++){
    476 		circle[i].ppx = circle[i].px = rand() * (float)win_width / (float)RAND_MAX;
    477 		circle[i].ppy = circle[i].py = rand() * (float)win_height / (float)RAND_MAX;
    478 		circle[i].vx = rand() * 100.0 / (float)RAND_MAX - 50;
    479 		circle[i].vy = rand() * 100.0 / (float)RAND_MAX - 50;
    480 		circle[i].r = rand() * 10.0 / (float)RAND_MAX;
    481 		circle[i].m = circle[i].r * circle[i].r;
    482 	}
    483 
    484 	while (next_menu == GAME_PLAY){
    485 		clock_gettime(CLOCK_MONOTONIC, &ts);
    486 		t0 = ts.tv_nsec;
    487 		receive_events(key_state);
    488 		handle_inputs(key_state);
    489 
    490 
    491 		clock_gettime(CLOCK_MONOTONIC, &ts);
    492 		t0 = ts.tv_nsec;
    493 
    494 		int collision[NUM_CIRCLE] = {0};
    495 		for (int k = 0; k < SUB_TICK; k++) {
    496 			for (int i = 0; i < NUM_CIRCLE; i++)
    497 				circle_next_tick(&circle[i], 1000 * 1000 * 1000 / FPS / SUB_TICK);
    498 
    499 			for (int i = 0; i < NUM_CIRCLE; i++)
    500 				for (int j = i + 1; j < NUM_CIRCLE; j++) {
    501 					circle_handle_collision_elastic(&circle[i], &circle[j]);
    502 					if (circle_test_collision(&circle[i], &circle[j]))
    503 						collision[i] = collision[j] = 1;
    504 				}
    505 		}
    506 		for (int i = 0; i < NUM_CIRCLE; i++)
    507 			if (collision[i] == 1)
    508 				XSetForeground(display, sgc[i], 0xFFFF00);
    509 			else
    510 				XSetForeground(display, sgc[i], 0xFF << i % 3 * 8);
    511 
    512 		// fix fps
    513 		// TODO: This method create some strange stripe when
    514 		// rendered in 60fps on a 60fps monitor.
    515 		dt = 0;
    516 		while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
    517 			clock_gettime(CLOCK_MONOTONIC, &ts);
    518 			t1 = ts.tv_nsec;
    519 			dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
    520 		}
    521 #ifdef COUNT_FPS
    522 		// count fps.
    523 		fps_count++;
    524 		if (t1 < t0){
    525 			printf("fps: %u\n", fps_count);
    526 			fps_count = 0;
    527 		}
    528 #endif
    529 
    530 		XClearArea(display, window,
    531 				   0, 0,                  // position
    532 				   win_width, win_height, // width and height
    533 				   False);
    534 		for (int i = 0; i < NUM_CIRCLE; i++) {
    535 			XDrawArc(display, window, sgc[i],
    536 						   circle[i].px - circle[i].r,
    537 						   circle[i].py - circle[i].r,    // position
    538 						   circle[i].r * 2, circle[i].r * 2,
    539 						   0, 360 << 6);   // width and height
    540 		}
    541 	}
    542 	XSetForeground(display, gc, 0x00FFFF);
    543 }
    544 
    545 void
    546 game_over(void)
    547 {
    548 	char *menu_char = "GAME OVER";
    549 
    550 	XClearArea(display, window,
    551 			   0, 0,                  // position
    552 			   win_width, win_height, // width and height
    553 			   False);
    554 	XDrawString(display, window, gc,
    555 				win_width/2 - strlen(menu_char)/2, win_height/2,
    556 				menu_char, strlen(menu_char));
    557 	XFlush(display);
    558 
    559 	sleep(1);
    560 	next_menu = START_MENU;
    561 }
    562 
    563 void
    564 cleanup(void)
    565 {
    566 	XCloseDisplay(display);
    567 }
    568 
    569 int
    570 main(void)
    571 {
    572 	setup();
    573 	while (next_menu != QUIT){
    574 		switch (next_menu){
    575 		case START_MENU:
    576 			start_menu();
    577 			break;
    578 		case GAME_PLAY:
    579 			game_play();
    580 			break;
    581 		case GAME_OVER:
    582 			game_over();
    583 			break;
    584 		default:
    585 			break;
    586 		}
    587 	}
    588 
    589 	cleanup();
    590 	return 0;
    591 }