commit dccf869678b3b7517425a0043f0e6a599fa6e47e
parent 2d2e38d5781ca2237479a9f57979f08aa3d77be9
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 30 Dec 2022 14:48:27 +0900
copy and add linked list
Diffstat:
A | ex8/Makefile | | | 14 | ++++++++++++++ |
A | ex8/main.c | | | 489 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ex8/main.h | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ex8/physics.c | | | 133 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ex8/world_map.h | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ex8/x.c | | | 137 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 930 insertions(+), 0 deletions(-)
diff --git a/ex8/Makefile b/ex8/Makefile
@@ -0,0 +1,14 @@
+INCS=-I/usr/X11R6/include
+CFLAGS=-Wall -W -Wextra -Wpointer-arith -Wbad-function-cast -std=c11
+LIBS=-L/usr/X11R6/lib -lX11 -lXext -lm
+IN=*.c
+OUT=main
+
+all: $(IN)
+ $(CC) $(INCS) $(CFLAGS) -o $(OUT) $(IN) $(LIBS)
+
+run: all
+ ./$(OUT)
+
+clean:
+ rm -f $(OUT)
diff --git a/ex8/main.c b/ex8/main.c
@@ -0,0 +1,489 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "main.h"
+#include "world_map.h"
+
+/* macros */
+#define FPS (60)
+#define SUB_TICK (4)
+#define NUM_BLOCK (200)
+#define GRAVITY (1000)
+
+/*
+#define COUNT_FPS
+*/
+
+enum keys {
+ KEY_D,
+ KEY_S,
+ KEY_A,
+ KEY_W,
+ KEY_Q,
+ KEY_SPACE,
+ NUM_KEY, //number of keys in this enum
+};
+enum key_state {
+ KEY_UP,
+ KEY_DOWN,
+};
+enum next_menu {
+ START_MENU,
+ GAME_PLAY,
+ GAME_OVER,
+ QUIT,
+};
+
+/* variables */
+struct Object block[NUM_BLOCK];
+struct Object player;
+int player_is_falling;
+enum next_menu next_menu = START_MENU;
+
+
+/* function prototypes */
+/* menus */
+void start_menu(void);
+void game_play(void);
+void game_over(void);
+/* events */
+void receive_events(enum key_state[]);
+void handle_inputs(enum key_state[]);
+/* sort */
+void sort_blocks(int colliding_blocks[], int distance[], int count);
+
+void
+start_menu(void)
+{
+ char *menu_char_q = "press q to quit.";
+ char *menu_char_s = "press <space> to start.";
+
+ x_clear_area();
+ x_draw_string(0x00FFFF,
+ WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2,
+ WORLD_HEIGHT * BLOCK_SIZE/2,
+ menu_char_q, strlen(menu_char_q));
+ x_draw_string(0x00FFFF,
+ WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2,
+ WORLD_HEIGHT * BLOCK_SIZE/2 + 20,
+ menu_char_s, strlen(menu_char_s));
+
+ while (next_menu == START_MENU) {
+ char c;
+ int event = x_next_event(&c);
+
+ switch (event) {
+ case XEXPOSE:
+ x_draw_string(0x00FFFF,
+ WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2,
+ WORLD_HEIGHT * BLOCK_SIZE/2,
+ menu_char_q, strlen(menu_char_q));
+ x_draw_string(0x00FFFF,
+ WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2,
+ WORLD_HEIGHT * BLOCK_SIZE/2 + 20,
+ menu_char_s, strlen(menu_char_s));
+
+ break;
+ case XWINDEL:
+ next_menu = QUIT;
+ break;
+ case XKEYPRESS:
+ switch (c) {
+ case 'q':
+ next_menu = QUIT;
+ break;
+ case ' ':
+ next_menu = GAME_PLAY;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void
+game_play(void)
+{
+ enum key_state key_state[NUM_KEY];
+ long t0, t1, dt;
+#ifdef COUNT_FPS
+ int fps_count = 0;
+#endif
+ struct timespec ts;
+
+ int bi = 0;
+ for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
+ if (world_map[i] == 'b') {
+ block[bi].pp.x = block[bi].p.x = i % WORLD_WIDTH * BLOCK_SIZE;
+ block[bi].pp.y = block[bi].p.y = i / WORLD_WIDTH * BLOCK_SIZE;
+ block[bi].a.x = 0;
+ block[bi].a.y = 0;
+ block[bi].v.x = 0;
+ block[bi].v.y = 0;
+ block[bi].body.rectangle.w = BLOCK_SIZE;
+ block[bi].body.rectangle.h = BLOCK_SIZE;
+ block[bi].m = block[bi].body.rectangle.w *
+ block[bi].body.rectangle.h;
+ bi++;
+ } else if (world_map[i] == 'p') {
+ player.pp.x = player.p.x = i % WORLD_WIDTH * BLOCK_SIZE;
+ player.pp.y = player.p.y = i / WORLD_WIDTH * BLOCK_SIZE;
+ player.v.x = 0;
+ player.v.y = 0;
+ player.a.x = 0;
+ player.a.y = GRAVITY;
+ player.body.rectangle.w = player.body.rectangle.h = BLOCK_SIZE;
+ player.m = player.body.rectangle.w * player.body.rectangle.h;
+ }
+ }
+
+ while (next_menu == GAME_PLAY){
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t0 = ts.tv_nsec;
+ receive_events(key_state);
+ handle_inputs(key_state);
+
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t0 = ts.tv_nsec;
+
+ for (int k = 0; k < SUB_TICK; k++) {
+ player_is_falling = object_is_falling(&player, block, NUM_BLOCK);
+ if (player_is_falling)
+ player.a.y = GRAVITY;
+ else
+ player.a.y = 0;
+ next_tick(&player, 1e9 / FPS / SUB_TICK);
+
+ // game over when fall out of the screen
+ if (player.p.y > WORLD_HEIGHT * BLOCK_SIZE) {
+ next_menu = GAME_OVER;
+ break;
+ }
+ // bind within the world
+ if (player.p.x < 0) {
+ player.p.x = 0;
+ }
+ if (WORLD_WIDTH * BLOCK_SIZE < player.p.x +
+ player.body.rectangle.w) {
+ player.p.x = WORLD_WIDTH * BLOCK_SIZE -
+ player.body.rectangle.w;
+ }
+
+
+ for (int i = 0; i < NUM_BLOCK; i++)
+ next_tick(&block[i], 1e9 / FPS / SUB_TICK);
+
+ int colliding_blocks[9];
+ int distance[9];
+ int j = 0;
+ for (int i = 0; i < NUM_BLOCK; i++)
+ if (test_collision(&player, &block[i])) {
+ colliding_blocks[j] = i;
+ distance[j] = (player.p.x - block[i].p.x) *
+ (player.p.x - block[i].p.x) +
+ (player.p.y - block[i].p.y) *
+ (player.p.y - block[i].p.y);
+ j++;
+ }
+ sort_blocks(colliding_blocks, distance, j);
+ for (int i = 0; i < j; i++)
+ handle_collision_mf(&player, &block[colliding_blocks[i]]);
+ }
+
+ // fix fps
+ // TODO: This method create some strange stripe when
+ // rendered in 60fps on a 60fps monitor.
+ dt = 0;
+ while (dt < 1.0 * 1e9 / FPS){
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t1 = ts.tv_nsec;
+ dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1e9;
+ }
+#ifdef COUNT_FPS
+ // count fps.
+ fps_count++;
+ if (t1 < t0){
+ printf("fps: %u\n", fps_count);
+ fps_count = 0;
+ }
+#endif
+
+ x_clear_area();
+ for (int i = 0; i < NUM_BLOCK; i++) {
+ x_draw_rectangle(0x00FF00,
+ block[i].p.x, block[i].p.y, // position
+ block[i].body.rectangle.w,
+ block[i].body.rectangle.h);
+ }
+ x_draw_rectangle(0x009FFF,
+ player.p.x, player.p.y, // position
+ player.body.rectangle.w, player.body.rectangle.h);
+ char status_string[128];
+ snprintf(status_string, 128, "falling: %d", player_is_falling);
+ x_draw_string(0x00FFFF,
+ 0, 20,
+ status_string,
+ strlen(status_string));
+ }
+}
+
+void
+game_over(void)
+{
+ char *menu_char = "GAME OVER";
+
+ x_clear_area();
+ x_draw_string(0x00FFFF,
+ WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char)/2,
+ WORLD_HEIGHT * BLOCK_SIZE/2,
+ menu_char, strlen(menu_char));
+ x_flush();
+
+ sleep(1);
+ next_menu = START_MENU;
+}
+
+void
+receive_events(enum key_state key_state[])
+{
+ while (x_pending() > 0) {
+ char c;
+ int event = x_next_event(&c);
+
+ switch (event) {
+ case XWINDEL:
+ next_menu = QUIT;
+ break;
+ case XKEYPRESS:
+ switch (c) {
+ case 'q':
+ key_state[KEY_Q] = KEY_DOWN;
+ break;
+ case 'd':
+ key_state[KEY_D] = KEY_DOWN;
+ break;
+ case 'a':
+ key_state[KEY_A] = KEY_DOWN;
+ break;
+ case 'w':
+ key_state[KEY_W] = KEY_DOWN;
+ break;
+ case 's':
+ key_state[KEY_S] = KEY_DOWN;
+ break;
+ case ' ':
+ key_state[KEY_SPACE] = KEY_DOWN;
+ break;
+ default:
+ break;
+ }
+ break;
+ case XKEYRELEASE:
+ switch (c) {
+ case 'q':
+ key_state[KEY_Q] = KEY_UP;
+ break;
+ case 'd':
+ key_state[KEY_D] = KEY_UP;
+ break;
+ case 'a':
+ key_state[KEY_A] = KEY_UP;
+ break;
+ case 'w':
+ key_state[KEY_W] = KEY_UP;
+ break;
+ case 's':
+ key_state[KEY_S] = KEY_UP;
+ break;
+ case ' ':
+ key_state[KEY_SPACE] = KEY_UP;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+void
+handle_inputs(enum key_state key_state[])
+{
+ if (key_state[KEY_Q] == KEY_DOWN){
+ next_menu = GAME_OVER;
+ return;
+ }
+ if (key_state[KEY_D] == KEY_DOWN) {
+ if (player.v.x > 0) {
+ player.a.x = 500;
+ } else {
+ player.a.x = 1000;
+ }
+ } else if (key_state[KEY_A] == KEY_DOWN) {
+ if (player.v.x > 0) {
+ player.a.x = -1000;
+ } else {
+ player.a.x = -500;
+ }
+ } else {
+ if (player_is_falling)
+ player.a.x = -player.v.x;
+ else
+ player.a.x = -3 * player.v.x;
+ }
+
+ if (player.v.x < -200) player.v.x = -200;
+ if (player.v.x > 200) player.v.x = 200;
+ /*
+ if (key_state[KEY_S] == KEY_DOWN)
+ player.v.y += 300;
+ if (key_state[KEY_W] == KEY_DOWN)
+ player.v.y += -300;
+ */
+ if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN)
+ player.v.y = -450;
+}
+
+void
+swap(int list[], int i, int j)
+{
+ int tmp = list[i];
+ list[i] = list[j];
+ list[j] = tmp;
+}
+
+void
+sort_blocks(int colliding_blocks[], int distance[], int count)
+{
+ for (int i = 0; i < count - 1; i++) {
+ for (int j = i + 1; j < count; j++) {
+ if (distance[i] > distance[j]) {
+ swap(colliding_blocks, i, j);
+ swap(distance, i, j);
+ }
+ }
+ }
+}
+
+void
+swap_ll(struct ObjectList *oi, struct ObjectList *oj)
+{
+ struct Object *tmp;
+ tmp = oi->o;
+ oi->o = oj->o;
+ oj->o = tmp;
+}
+
+int
+object_dist(struct Object *o1, struct Object *o2)
+{
+ return (o1->p.x - o2->p.x) * (o1->p.x - o2->p.x) +
+ (o1->p.y - o2->p.y) * (o1->p.y - o2->p.y);
+}
+
+void
+sort_ll(struct ObjectList *ol, struct Object *player)
+{
+ struct ObjectList *oi, *oj;
+ for (oi = ol; oi->next != NULL; oi = oi->next) {
+ for (oj = oi->next; oj != NULL; oj = oj->next) {
+ if (object_dist(oj->o, player) < object_dist(oi->o, player))
+ swap_ll(oi, oj);
+ }
+ }
+}
+
+int
+main(void)
+{
+ struct ObjectList *blh, *blc;
+ int bi = 0;
+
+ blh = (struct ObjectList *) malloc(sizeof(struct ObjectList));
+ blc = blh;
+
+ for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
+ if (world_map[i] == 'b') {
+ if (bi > 0) {
+ blc->next = (struct ObjectList *)malloc(sizeof(struct ObjectList));
+ blc = blc->next;
+ }
+ struct Object *block;
+ // TODO: don't forget to free these things.
+ block = (struct Object *)malloc(sizeof(struct Object));
+ block->pp.x = block->p.x = i % WORLD_WIDTH * BLOCK_SIZE;
+ block->pp.y = block->p.y = i / WORLD_WIDTH * BLOCK_SIZE;
+ block->a.x = 0;
+ block->a.y = 0;
+ block->v.x = 0;
+ block->v.y = 0;
+ block->body.rectangle.w = BLOCK_SIZE;
+ block->body.rectangle.h = BLOCK_SIZE;
+ block->m = block->body.rectangle.w *
+ block->body.rectangle.h;
+ blc->o = block;
+ bi++;
+ } else if (world_map[i] == 'p') {
+ player.pp.x = player.p.x = i % WORLD_WIDTH * BLOCK_SIZE;
+ player.pp.y = player.p.y = i / WORLD_WIDTH * BLOCK_SIZE;
+ player.v.x = 0;
+ player.v.y = 0;
+ player.a.x = 0;
+ player.a.y = GRAVITY;
+ player.body.rectangle.w = player.body.rectangle.h = BLOCK_SIZE;
+ player.m = player.body.rectangle.w * player.body.rectangle.h;
+ }
+ }
+
+
+ blc = blh;
+ while (blc != NULL) {
+ printf("blc->o->p: (%f, %f)\n", blc->o->p.x, blc->o->p.y);
+ blc = blc->next;
+ }
+
+ sort_ll(blh, &player);
+ blc = blh;
+ printf("blc: %p", blc);
+ while (blc != NULL) {
+ printf("blc->o->p: (%4.0f, %4.0f) %d\n", blc->o->p.x, blc->o->p.y,
+ object_dist(blc->o, &player));
+ blc = blc->next;
+ }
+
+ return 0;
+}
+
+int
+main2(void)
+{
+ x_setup_window(0, 0,
+ WORLD_WIDTH * BLOCK_SIZE, WORLD_HEIGHT * BLOCK_SIZE,
+ 0x000000, "UNKO");
+ while (next_menu != QUIT){
+ switch (next_menu){
+ case START_MENU:
+ start_menu();
+ break;
+ case GAME_PLAY:
+ game_play();
+ break;
+ case GAME_OVER:
+ game_over();
+ break;
+ default:
+ break;
+ }
+ }
+
+ x_clean_up();
+ return 0;
+}
diff --git a/ex8/main.h b/ex8/main.h
@@ -0,0 +1,92 @@
+/*
+ * physics.c
+ */
+enum object_shape {
+ SRECTANGLE,
+ STRIANGLE,
+ SCIRCLE,
+};
+
+struct Point {
+ float x;
+ float y;
+};
+
+struct Rectangle { // origin is top left corner
+ int w, h;
+};
+
+struct Triangle {
+ struct Point v2;
+ struct Point v3;
+};
+
+struct Circle { // origin is center
+ float r;
+};
+
+union Body {
+ struct Circle circle;
+ struct Triangle triangle;
+ struct Rectangle rectangle;
+};
+
+struct Object {
+ enum object_shape shape;
+ struct Point pp;
+ struct Point p;
+ struct Point v;
+ struct Point a;
+ union Body body;
+ int m;
+};
+
+struct ObjectList {
+ struct Object *o;
+ struct ObjectList *next;
+};
+
+struct rect {
+ struct Point pp;
+ struct Point p;
+ struct Point v;
+ struct Point a;
+ int w, h;
+ int m;
+};
+
+struct circle {
+ struct Point pp;
+ struct Point p;
+ struct Point v;
+ struct Point a;
+ int r;
+ int m;
+};
+
+int test_collision(struct Object *, struct Object *); // 1 if collide, 0 if not
+void next_tick(struct Object *o, long);
+void handle_collision_mf(struct Object *, struct Object *);
+int object_is_falling(struct Object *o, struct Object *fb, int num_f);
+
+/*
+ * x.c
+ */
+enum event_type {
+ XKEYPRESS,
+ XKEYRELEASE,
+ XEXPOSE,
+ XWINDEL,
+ NOEVENT,
+};
+
+int x_setup_window(int, int, unsigned int, unsigned int, unsigned long, char *);
+void x_clear_area(void);
+void x_draw_string(unsigned long color, int x, int y, const char *str, int length);
+void x_draw_rectangle(unsigned long color, int x, int y,
+ unsigned int w, unsigned int h);
+void x_flush(void);
+void x_clean_up(void);
+int x_next_event(char *c);
+int x_pending(void);
+void x_get_win_wh(int *, int *);
diff --git a/ex8/physics.c b/ex8/physics.c
@@ -0,0 +1,133 @@
+#include <stdio.h>
+
+#include "main.h"
+
+static int test_collision_rr(struct Object *, struct Object *);
+static void rect_next_tick(struct Object *, long);
+static void rect_handle_collision_mf(struct Object *, struct Object *);
+
+/*
+ * 1 if collide, 0 if not
+ */
+int
+test_collision(struct Object *o1, struct Object *o2)
+{
+ if (o1->shape == SRECTANGLE && o2->shape == SRECTANGLE)
+ return test_collision_rr(o1, o2);
+ else {
+ fprintf(stderr, "test_collision for other shapes is not implemented yet\n");
+ return -1;
+ }
+}
+
+static int
+test_collision_rr(struct Object *o1, struct Object *o2)
+{
+ if (o1->shape != SRECTANGLE || o2->shape != SRECTANGLE) {
+ fprintf(stderr, "test_collision_rr: invalid objects\n");
+ return -1;
+ }
+ return o1->p.x < o2->p.x + o2->body.rectangle.w &&
+ o2->p.x < o1->p.x + o1->body.rectangle.w &&
+ o2->p.y < o1->p.y + o1->body.rectangle.h &&
+ o1->p.y < o2->p.y + o2->body.rectangle.h;
+}
+
+void
+next_tick(struct Object *o, long ndt)
+{
+ switch (o->shape) {
+ case SRECTANGLE:
+ rect_next_tick(o, ndt);
+ break;
+ default:
+ fprintf(stderr, "next_tick: not implemented for other shapes\n");
+ break;
+ }
+}
+
+static void
+rect_next_tick(struct Object *o, long ndt)
+{
+ if (o->shape != SRECTANGLE) {
+ fprintf(stderr, "rect_next_tick: invalid object shape\n");
+ return;
+ }
+ o->pp.x = o->p.x;
+ o->pp.y = o->p.y;
+ o->v.x += o->a.x * ndt / 1e9;
+ o->v.y += o->a.y * ndt / 1e9;
+ o->p.x += o->v.x * ndt / 1e9;
+ o->p.y += o->v.y * ndt / 1e9;
+}
+
+void
+handle_collision_mf(struct Object *om, struct Object *of)
+{
+ // TODO: too many testing?
+ if (!test_collision(om, of))
+ return;
+ if (om->shape == SRECTANGLE && of->shape == SRECTANGLE)
+ rect_handle_collision_mf(om, of);
+ else {
+ fprintf(stderr, "handle_collision_mf: not implemented for other shapes\n");
+ return;
+ }
+}
+
+/*
+ * Handle collision of a moving rect against fixed rect
+ */
+static void
+rect_handle_collision_mf(struct Object *om, struct Object *of)
+{
+ // TODO: too many testing?
+ if (!test_collision(om, of))
+ return;
+ if (om->pp.x + om->body.rectangle.w <= of->pp.x &&
+ of->p.x < om->p.x + om->body.rectangle.w) {
+ // collisioin from left to right
+ om->p.x = of->p.x - om->body.rectangle.w;
+ om->v.x = 0;
+ }
+ if (of->pp.x + of->body.rectangle.w <= om->pp.x &&
+ om->p.x < of->p.x + of->body.rectangle.w) {
+ // collision from right to left
+ om->p.x = of->p.x + of->body.rectangle.w;
+ om->v.x = 0;
+ }
+
+ if (om->pp.y + om->body.rectangle.h <= of->pp.y &&
+ of->p.y < om->p.y + om->body.rectangle.h) {
+ // collision from up to down
+ om->p.y = of->p.y - om->body.rectangle.h;
+ om->v.y = 0;
+ }
+ if (of->pp.y + of->body.rectangle.h <= om->pp.y &&
+ om->p.y < of->p.y + of->body.rectangle.h) {
+ // collision from down to up
+ om->p.y = of->p.y + of->body.rectangle.h;
+ om->v.y = 0;
+ }
+
+}
+
+/*
+ * Test if object o is not on top of one of fb (floor blocks).
+ * o: object to be tested, fb: floor blocks, num_f: count of fb.
+ */
+int
+object_is_falling(struct Object *o, struct Object *fb, int num_f)
+{
+ struct Object p = *o;
+
+ p.v.x = 0; p.v.y = 1;
+ p.a.x = 0; p.a.y = 0;
+
+ next_tick(&p, 1e9);
+
+ for (int i = 0; i < num_f; i++)
+ if (test_collision(&p, &fb[i]))
+ return 0;
+ return 1;
+}
diff --git a/ex8/world_map.h b/ex8/world_map.h
@@ -0,0 +1,65 @@
+#define WORLD_WIDTH (80)
+#define WORLD_HEIGHT (60)
+#define BLOCK_SIZE (10)
+
+char world_map[WORLD_WIDTH * WORLD_HEIGHT + 1] =
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................b..............................................................."
+"..........................................................b..........b.........."
+"................................................................................"
+".......................b........................................................"
+"...........................................b...................................."
+"...........................................b...................................."
+"................................................................................"
+"..................b............................................................."
+"................................................................................"
+"...........................................b...................................."
+"................................................................................"
+"................................................................................"
+"...........................b...................................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"....................................bbbbbbbbbb.................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"..............................................bbbbbbbbbb........................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"....................................bbbbbbbbbb.................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"..........................bbbbbbbbbb............................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................bbbbbbbbbb......................................................"
+"................................................................................"
+"................................................................................"
+"...p............................................................................"
+"bbbbbbbbbbbbbbbbbbbbbbbbb.......bbbbbbbbbbbbbbbbbbbbbbbb...bbbbbbbbbbbbbbbbbbbbb"
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b....................";
diff --git a/ex8/x.c b/ex8/x.c
@@ -0,0 +1,137 @@
+#include <stdio.h>
+
+#include <X11/Xlib.h>
+
+#include "main.h"
+
+Display *display;
+Atom wm_delete_window;
+GC gc;
+Window window;
+unsigned int win_width, win_height;
+
+/*
+ * Creates a window
+ * If succeeded, returns 0
+ * If error occured, returns -1
+ */
+int
+x_setup_window(int x, int y,
+ unsigned int w, unsigned int h,
+ unsigned long bc, char *win_name)
+{
+ if ((display = XOpenDisplay(NULL)) == NULL){
+ fprintf(stderr, "ERROR: could not open display\n");
+ return -1;
+ }
+ window = XCreateSimpleWindow(
+ display,
+ XDefaultRootWindow(display),
+ x, y,
+ w, h,
+ 0, 0, // I don't need border?
+ bc);
+ win_width = w;
+ win_height = h;
+
+ XStoreName(display, window, win_name);
+ gc = XCreateGC(display, window, 0, NULL);
+
+ wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display, window, &wm_delete_window, 1);
+
+ XSelectInput(display, window,
+ ExposureMask | KeyPressMask | KeyReleaseMask);
+
+ XMapWindow(display, window);
+ return 0;
+}
+
+/*
+ * Clears the window with the background color.
+ * I don't know what the return value of XClearArea means...
+ */
+void
+x_clear_area(void)
+{
+ XClearArea(display, window,
+ 0, 0,
+ win_width, win_height,
+ False);
+}
+
+/*
+ * Draws a string with specified color.
+ */
+void
+x_draw_string(unsigned long color, int x, int y, const char *str, int length)
+{
+ XSetForeground(display, gc, color);
+ XDrawString(display, window, gc, x, y, str, length);
+}
+
+void
+x_draw_rectangle(unsigned long color, int x, int y,
+ unsigned int w, unsigned int h)
+{
+ XSetForeground(display, gc, color);
+ XDrawRectangle(display, window, gc, x, y, w, h);
+}
+
+void
+x_flush(void)
+{
+ XFlush(display);
+}
+
+void
+x_clean_up(void)
+{
+ XCloseDisplay(display);
+}
+
+int
+x_next_event(char *c)
+{
+ XEvent event;
+ XWindowAttributes wattr;
+
+ XNextEvent(display, &event);
+ switch (event.type) {
+ case Expose:
+ XGetWindowAttributes(display, window, &wattr);
+ win_width = wattr.width;
+ win_height = wattr.height;
+ return XEXPOSE;
+ break;
+ case ClientMessage:
+ if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
+ return XWINDEL;
+ }
+ break;
+ case KeyPress:
+ *c = XLookupKeysym(&event.xkey, 0);
+ return XKEYPRESS;
+ break;
+ case KeyRelease:
+ *c = XLookupKeysym(&event.xkey, 0);
+ return XKEYRELEASE;
+ break;
+ default:
+ break;
+ }
+ return NOEVENT;
+}
+
+int
+x_pending(void)
+{
+ return XPending(display);
+}
+
+void
+x_get_win_wh(int *w, int *h)
+{
+ *w = win_width;
+ *h = win_height;
+}