pong.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2015 K. Lange
  5. *
  6. * pong - Window Manager Pong
  7. *
  8. * Play pong where the paddles and ball are all windows.
  9. * Use the WM bindings to drag the left paddle to play.
  10. * Press `q` to quit.
  11. *
  12. * Rendering updates are all done by the compositor, while the game
  13. * only renders to the windows once at start up.
  14. *
  15. * Window movement tracking keeps the game logic aware of the paddle
  16. * position, and window moves for the ball and other paddle keep
  17. * things in the right place visually.
  18. *
  19. */
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <assert.h>
  23. #include <unistd.h>
  24. #include <fcntl.h>
  25. #include <time.h>
  26. #include <math.h>
  27. #include <sched.h>
  28. #include <sys/time.h>
  29. #include <toaru/yutani.h>
  30. #include <toaru/graphics.h>
  31. #define GAME_PATH "/usr/share/pong"
  32. #define PADDLE_WIDTH 50
  33. #define PADDLE_HEIGHT 300
  34. #define BALL_SIZE 50
  35. static yutani_t * yctx;
  36. static int spkr = 0;
  37. struct object {
  38. double x;
  39. double y;
  40. int width;
  41. int height;
  42. double vel_x;
  43. double vel_y;
  44. sprite_t sprite;
  45. };
  46. static yutani_window_t * paddle_left;
  47. static yutani_window_t * paddle_right;
  48. static yutani_window_t * ball_win;
  49. static struct object left;
  50. static struct object right;
  51. static struct object ball;
  52. static gfx_context_t * paddle_left_ctx;
  53. static gfx_context_t * paddle_right_ctx;
  54. static gfx_context_t * ball_ctx;
  55. static int should_exit = 0;
  56. static int left_score = 0;
  57. static int right_score = 0;
  58. struct spkr {
  59. int length;
  60. int frequency;
  61. };
  62. static void note(int frequency) {
  63. struct spkr s = {
  64. .length = 2,
  65. .frequency = frequency,
  66. };
  67. write(spkr, &s, sizeof(s));
  68. }
  69. static uint32_t current_time() {
  70. static uint32_t start_time = 0;
  71. static uint32_t start_subtime = 0;
  72. struct timeval t;
  73. gettimeofday(&t, NULL);
  74. if (!start_time) {
  75. start_time = t.tv_sec;
  76. start_subtime = t.tv_usec;
  77. }
  78. uint32_t sec_diff = t.tv_sec - start_time;
  79. uint32_t usec_diff = t.tv_usec - start_subtime;
  80. if (t.tv_usec < (int)start_subtime) {
  81. sec_diff -= 1;
  82. usec_diff = (1000000 + t.tv_usec) - start_subtime;
  83. }
  84. return (uint32_t)(sec_diff * 1000 + usec_diff / 1000);
  85. }
  86. static int colliding(struct object * a, struct object * b) {
  87. if (a->x >= b->x + b->width) return 0;
  88. if (a->y >= b->y + b->height) return 0;
  89. if (b->x >= a->x + a->width) return 0;
  90. if (b->y >= a->y + a->height) return 0;
  91. return 1;
  92. }
  93. void redraw(void) {
  94. draw_fill(paddle_left_ctx, rgba(0,0,0,0));
  95. draw_fill(paddle_right_ctx, rgba(0,0,0,0));
  96. draw_fill(ball_ctx, rgba(0,0,0,0));
  97. draw_sprite(paddle_left_ctx, &left.sprite, 0, 0);
  98. draw_sprite(paddle_right_ctx, &right.sprite, 0, 0);
  99. draw_sprite(ball_ctx, &ball.sprite, 0, 0);
  100. yutani_flip(yctx, paddle_left);
  101. yutani_flip(yctx, paddle_right);
  102. yutani_flip(yctx, ball_win);
  103. }
  104. void update_left(void) {
  105. yutani_window_move(yctx, paddle_left, left.x, left.y);
  106. }
  107. void update_right(void) {
  108. yutani_window_move(yctx, paddle_right, right.x, right.y);
  109. }
  110. void update_ball(void) {
  111. yutani_window_move(yctx, ball_win, ball.x, ball.y);
  112. }
  113. void update_stuff(void) {
  114. right.vel_y = (right.y + right.height / 2 < ball.y + ball.height / 2) ? 2.0 : -2.0;
  115. right.y += right.vel_y;
  116. update_right();
  117. ball.x += ball.vel_x;
  118. ball.y += ball.vel_y;
  119. if (ball.y < 0) {
  120. ball.vel_y = -ball.vel_y;
  121. ball.y = 0;
  122. }
  123. if (ball.y > yctx->display_height - ball.height) {
  124. ball.vel_y = -ball.vel_y;
  125. ball.y = yctx->display_height - ball.height;
  126. }
  127. if (ball.x < 0) {
  128. ball.x = yctx->display_width / 2 - ball.width / 2;
  129. ball.y = yctx->display_height / 2 - ball.height / 2;
  130. ball.vel_x = -10.0;
  131. ball.vel_y = ((double)rand() / RAND_MAX) * 6.0 - 3.0;
  132. note(10000);
  133. right_score++;
  134. printf("%d : %d\n", left_score, right_score);
  135. }
  136. if (ball.x > yctx->display_width - ball.width ) {
  137. ball.x = yctx->display_width / 2 - ball.width / 2;
  138. ball.y = yctx->display_height / 2 - ball.height / 2;
  139. ball.vel_x = 10.0;
  140. ball.vel_y = ((double)rand() / RAND_MAX) * 6.0 - 3.0;
  141. note(17000);
  142. left_score++;
  143. printf("%d : %d\n", left_score, right_score);
  144. }
  145. if (colliding(&ball, &left)) {
  146. ball.x = left.x + left.width + 2;
  147. ball.vel_x = (abs(ball.vel_x) < 8.0) ? -ball.vel_x * 1.05 : -ball.vel_x;
  148. double intersect = ((ball.y + ball.height/2) - (left.y)) / ((double)left.height) - 0.5;
  149. ball.vel_y = intersect * 8.0;
  150. note(15680);
  151. }
  152. if (colliding(&ball, &right)) {
  153. ball.x = right.x - ball.width - 2;
  154. ball.vel_x = (abs(ball.vel_x) < 8.0) ? -ball.vel_x * 1.05 : -ball.vel_x;
  155. double intersect = ((ball.y + ball.height/2) - (right.y)) / ((double)right.height/2.0);
  156. ball.vel_y = intersect * 3.0;
  157. note(11747);
  158. }
  159. update_ball();
  160. }
  161. int main (int argc, char ** argv) {
  162. yctx = yutani_init();
  163. left.width = PADDLE_WIDTH;
  164. left.height = PADDLE_HEIGHT;
  165. right.width = PADDLE_WIDTH;
  166. right.height = PADDLE_HEIGHT;
  167. ball.width = BALL_SIZE;
  168. ball.height = BALL_SIZE;
  169. ball.x = yctx->display_width / 2 - ball.width / 2;
  170. ball.y = yctx->display_height / 2 - ball.height / 2;
  171. left.x = 10;
  172. left.y = yctx->display_height / 2 - left.height / 2;
  173. right.x = yctx->display_width - right.width - 10;
  174. right.y = yctx->display_height / 2 - right.height / 2;
  175. paddle_left = yutani_window_create(yctx, PADDLE_WIDTH, PADDLE_HEIGHT);
  176. paddle_right = yutani_window_create(yctx, PADDLE_WIDTH, PADDLE_HEIGHT);
  177. ball_win = yutani_window_create(yctx, BALL_SIZE, BALL_SIZE);
  178. paddle_left_ctx = init_graphics_yutani(paddle_left);
  179. paddle_right_ctx = init_graphics_yutani(paddle_right);
  180. ball_ctx = init_graphics_yutani(ball_win);
  181. srand(time(NULL));
  182. ball.vel_y = ((double)rand() / RAND_MAX) * 6.0 - 3.0;
  183. ball.vel_x = -10.0;
  184. fprintf(stderr, "Loading sprites...\n");
  185. load_sprite(&left.sprite, GAME_PATH "/paddle-red.bmp");
  186. left.sprite.alpha = ALPHA_EMBEDDED;
  187. load_sprite(&right.sprite,GAME_PATH "/paddle-blue.bmp");
  188. right.sprite.alpha = ALPHA_EMBEDDED;
  189. load_sprite(&ball.sprite, GAME_PATH "/ball.bmp");
  190. ball.sprite.alpha = ALPHA_EMBEDDED;
  191. redraw();
  192. update_left();
  193. update_right();
  194. update_ball();
  195. uint32_t last_tick = current_time();
  196. spkr = open("/dev/spkr", O_WRONLY);
  197. while (!should_exit) {
  198. uint32_t t = current_time();
  199. if (t > last_tick + 10) {
  200. last_tick += 10;
  201. update_stuff();
  202. }
  203. yutani_msg_t * m = yutani_poll_async(yctx);
  204. if (m) {
  205. switch (m->type) {
  206. case YUTANI_MSG_KEY_EVENT:
  207. {
  208. struct yutani_msg_key_event * ke = (void*)m->data;
  209. if (ke->event.key == 'q' && ke->event.action == KEY_ACTION_DOWN) {
  210. should_exit = 1;
  211. }
  212. }
  213. break;
  214. case YUTANI_MSG_WINDOW_MOVE:
  215. {
  216. struct yutani_msg_window_move * wm = (void*)m->data;
  217. if (wm->wid == paddle_left->wid) {
  218. /* Update paddle speed and position */
  219. left.y = (double)wm->y;
  220. if (wm->x != (int)left.x) {
  221. update_left();
  222. }
  223. }
  224. }
  225. break;
  226. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  227. {
  228. struct yutani_msg_window_mouse_event * me = (void*)m->data;
  229. if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
  230. if (me->wid == paddle_left->wid) {
  231. yutani_window_drag_start(yctx, paddle_left);
  232. }
  233. }
  234. }
  235. break;
  236. case YUTANI_MSG_SESSION_END:
  237. should_exit = 1;
  238. break;
  239. default:
  240. break;
  241. }
  242. free(m);
  243. } else {
  244. sched_yield();
  245. }
  246. }
  247. yutani_close(yctx, paddle_left);
  248. yutani_close(yctx, paddle_right);
  249. yutani_close(yctx, ball_win);
  250. return 0;
  251. }