From cb2376a82f1f556f23c47d435cd60af2abfc0138 Mon Sep 17 00:00:00 2001 From: yzrh Date: Wed, 30 Dec 2020 15:54:00 +0000 Subject: Rename binary. --- CHANGE.md | 5 + src/Makefile | 18 +- src/main.c | 1542 ------------------------------------------------------- src/snake-sdl.c | 1542 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/version.h | 2 +- 5 files changed, 1557 insertions(+), 1552 deletions(-) delete mode 100644 src/main.c create mode 100644 src/snake-sdl.c diff --git a/CHANGE.md b/CHANGE.md index b4ab6b3..a0c1405 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,3 +1,8 @@ +0.6.2_5 (2020-12-30) +==================== + +* Rename binary. + 0.6.2_4 (2020-12-25) ==================== diff --git a/src/Makefile b/src/Makefile index 37533bd..f867d21 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -src = main.c collision.c game.c input.c renderer.c +src = snake-sdl.c collision.c game.c input.c renderer.c inc = extern.h game.h input.h renderer.h screen.h version.h .ifdef GPIO @@ -31,20 +31,20 @@ CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/lib all: ${obj} ${inc} - ${CC} ${LDFLAGS} -o snake ${obj} + ${CC} ${LDFLAGS} -o snake-sdl ${obj} clean: - rm -f snake ${obj} + rm -f snake-sdl ${obj} install: install -d ${PREFIX}/bin - install -d ${PREFIX}/share/snake - install snake ${PREFIX}/bin - install -m644 ../contrib/font.ttf ${PREFIX}/share/snake - install -m644 ../contrib/music.flac ${PREFIX}/share/snake + install -d ${PREFIX}/share/snake-sdl + install snake-sdl ${PREFIX}/bin + install -m644 ../contrib/font.ttf ${PREFIX}/share/snake-sdl + install -m644 ../contrib/music.flac ${PREFIX}/share/snake-sdl deinstall: - rm -f ${PREFIX}/bin/snake - rm -rf ${PREFIX}/share/snake + rm -f ${PREFIX}/bin/snake-sdl + rm -rf ${PREFIX}/share/snake-sdl .PHONY: all clean install deinstall diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 48b8fc8..0000000 --- a/src/main.c +++ /dev/null @@ -1,1542 +0,0 @@ -/* - * Copyright (c) 2019-2020, yzrh - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ - -#include -#include -#include - -#endif /* __linux__ */ - -#include -#include - -#ifdef SOUND - -#include - -#endif /* SOUND */ - -#include "version.h" -#include "extern.h" -#include "screen.h" - -#ifdef GPIO - -#include "gpio.h" - -#endif /* GPIO */ - -static pthread_mutex_t mutex; -static pthread_cond_t cond; - -static SDL_Renderer *renderer; -static TTF_Font *font_20; -static TTF_Font *font_24; - -/* - * Default to keyboard, - * GPIO inputs are mapped to controller keys - */ -static Snake_Device device = SNAKE_KEYBOARD; -static int selection = 0; - -static Snake_Text text_title; -static Snake_Text text_help_top; -static Snake_Text text_help_0; -static Snake_Text text_help_1; -static Snake_Text text_help_2; -static Snake_Text text_help_3; -static Snake_Text text_help_4; -static Snake_Text text_help_5; -static Snake_Text text_help_6; -static Snake_Text text_help_7; - -static Snake_Text text_score; - -static Snake_Text text_prompt_start; -static Snake_Text text_prompt_resume; - -static Snake_Text text_prompt_0; -static Snake_Text text_prompt_1; -static Snake_Text text_prompt_2; - -static bool start = 1; -static bool end = 0; -static bool begin = 0; - -static bool god = 0; -static bool reset = 0; -static bool wait = 0; -static bool multiplayer = 0; -static bool flappy = 0; - -static struct timespec delay = { - .tv_sec = 0, - .tv_nsec = TICK_TIME_INIT -}; - -static int score; -static int result; -static int distance; -static int speed; - -static Snake_Input input = SNAKE_EMPTY; -static Snake_Input head[2][2]; - -static Snake_Pos *snake0 = NULL; -static Snake_Pos *snake1 = NULL; - -static int fruit[2]; - -static Snake_Pos *gap = NULL; - -static void* -handler_rendering(void *args) -{ - const SDL_Color fg = { - COLOUR_FOREGROUND_R, - COLOUR_FOREGROUND_G, - COLOUR_FOREGROUND_B - }; - const SDL_Color bg = { - COLOUR_BACKGROUND_R, - COLOUR_BACKGROUND_G, - COLOUR_BACKGROUND_B - }; - const SDL_Color bg_s = { - COLOUR_BACKGROUND_SHADE_R, - COLOUR_BACKGROUND_SHADE_G, - COLOUR_BACKGROUND_SHADE_B - }; - - char result_str[23]; - char score_str[11]; - - Snake_Text text_result; - - Snake_Text text_welcome; - Snake_Text text_clear; - Snake_Text text_gameover; - Snake_Text text_pause; - - while (!end) { - draw_clear(renderer); - draw_colour(renderer, SNAKE_FOREGROUND); - - draw_wall(renderer); - - draw_text(renderer, - font_20, - SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, - SCREEN_UNIT_Y, - "Snake", - 1, - 0, - fg, - bg, - &text_title); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, - 2 * SCREEN_UNIT_Y, - "Help", - 1, - 0, - fg, - bg, - &text_help_top); - if (god) - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 2.5 * SCREEN_UNIT_Y, - "0. Don't forget to cheat", - 0, - 0, - fg, - bg, - &text_help_0); - if (device == SNAKE_KEYBOARD) { - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3 * SCREEN_UNIT_Y, - "1. Classic: use arrow keys to move", - 0, - 0, - fg, - bg, - &text_help_1); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3.5 * SCREEN_UNIT_Y, - "2. Multiplayer: use s, e, d, f keys", - 0, - 0, - fg, - bg, - &text_help_2); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4 * SCREEN_UNIT_Y, - "3. Flappy: SPACE key to jump", - 0, - 0, - fg, - bg, - &text_help_3); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4.5 * SCREEN_UNIT_Y, - "4. ESCAPE key to pause", - 0, - 0, - fg, - bg, - &text_help_4); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5 * SCREEN_UNIT_Y, - "5. r key to reset", - 0, - 0, - fg, - bg, - &text_help_5); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5.5 * SCREEN_UNIT_Y, - "6. m key to toggle music", - 0, - 0, - fg, - bg, - &text_help_6); - } else if (device == SNAKE_TOUCHSCREEN) { - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3 * SCREEN_UNIT_Y, - "1. Classic: swipe to move", - 0, - 0, - fg, - bg, - &text_help_1); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3.5 * SCREEN_UNIT_Y, - "2. Multiplayer: use different side", - 0, - 0, - fg, - bg, - &text_help_2); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4 * SCREEN_UNIT_Y, - "3. Flappy: tap to jump", - 0, - 0, - fg, - bg, - &text_help_3); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4.5 * SCREEN_UNIT_Y, - "4. Tap here to pause", - 0, - 1, - fg, - bg, - &text_help_4); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5 * SCREEN_UNIT_Y, - "5. Tap here to reset", - 0, - 1, - fg, - bg, - &text_help_5); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5.5 * SCREEN_UNIT_Y, - "6. Tap here to toggle music", - 0, - 1, - fg, - bg, - &text_help_6); - } else if (device == SNAKE_CONTROLLER) { - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3 * SCREEN_UNIT_Y, - "1. Classic: use D-pad to move", - 0, - 0, - fg, - bg, - &text_help_1); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 3.5 * SCREEN_UNIT_Y, - "2. Multiplayer: second controller", - 0, - 0, - fg, - bg, - &text_help_2); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4 * SCREEN_UNIT_Y, - "3. Flappy: Press A to jump", - 0, - 0, - fg, - bg, - &text_help_3); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 4.5 * SCREEN_UNIT_Y, - "4. Press BACK to pause", - 0, - 0, - fg, - bg, - &text_help_4); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5 * SCREEN_UNIT_Y, - "5. Press X to reset", - 0, - 0, - fg, - bg, - &text_help_5); - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 5.5 * SCREEN_UNIT_Y, - "6. Press Y to toggle music", - 0, - 0, - fg, - bg, - &text_help_6); - } - draw_text(renderer, - font_20, - SCREEN_HEIGHT + SCREEN_UNIT_X / 4, - 6 * SCREEN_UNIT_Y, - "7. Have fun", - 0, - 0, - fg, - bg, - &text_help_7); - - snprintf(score_str, 11, "Score: %d", score); - - draw_text(renderer, - font_20, - SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, - 7 * SCREEN_UNIT_Y, - score_str, - 1, - 0, - fg, - bg, - &text_score); - - draw_snake(renderer, &snake0); - - if (flappy) { - draw_pipe(renderer, &gap); - } else if (multiplayer) - draw_snake(renderer, &snake1); - else if (!start) - draw_fruit(renderer, fruit); - - if (start || reset) { - if (multiplayer) { - if (result == 3) - snprintf(result_str, 23, "Draw"); - else if (result > 0) - snprintf(result_str, 23, - "%s is the winner", - result == 1 ? - "Player 2" : "Player 1"); - else - snprintf(result_str, 23, "Reset"); - text_result.cache = 0; - - draw_text(renderer, - font_24, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 - - 0.75 * SCREEN_UNIT_Y, - result_str, - 1, - 0, - fg, - bg_s, - &text_result); - } - if (start) - draw_text(renderer, - font_24, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2, - "Welcome", - 1, - 0, - fg, - bg_s, - &text_welcome); - else if (!flappy && !multiplayer && - score == SCREEN_BLOCK / 2) - draw_text(renderer, - font_24, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2, - "Why don't you have legs?", - 1, - 0, - fg, - bg_s, - &text_clear); - else - draw_text(renderer, - font_24, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2, - "Game over!", - 1, - 0, - fg, - bg_s, - &text_gameover); - if (device == SNAKE_KEYBOARD) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Press RETURN key for selection", - 1, - 0, - fg, - bg_s, - &text_prompt_start); - else if (device == SNAKE_TOUCHSCREEN) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Tap the option to select", - 1, - 0, - fg, - bg_s, - &text_prompt_start); - else if (device == SNAKE_CONTROLLER) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Press START to select", - 1, - 0, - fg, - bg_s, - &text_prompt_start); - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + 1.5 * SCREEN_UNIT_Y, - "Classic", - 1, - device == SNAKE_TOUCHSCREEN || - selection == 0 ? 1 : 0, - fg, - bg_s, - &text_prompt_0); - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + 2.25 * SCREEN_UNIT_Y, - "Multiplayer", - 1, - device == SNAKE_TOUCHSCREEN || - selection == 1 ? 1 : 0, - fg, - bg_s, - &text_prompt_1); - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + 3 * SCREEN_UNIT_Y, - "Flappy", - 1, - device == SNAKE_TOUCHSCREEN || - selection == 2 ? 1 : 0, - fg, - bg_s, - &text_prompt_2); - } else if (wait) { - draw_text(renderer, - font_24, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2, - "Paused", - 1, - 0, - fg, - bg_s, - &text_pause); - if (device == SNAKE_KEYBOARD) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Press ESCAPE key to resume", - 1, - 0, - fg, - bg_s, - &text_prompt_resume); - else if (device == SNAKE_TOUCHSCREEN) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Tap here to resume", - 1, - 1, - fg, - bg_s, - &text_prompt_resume); - else if (device == SNAKE_CONTROLLER) - draw_text(renderer, - font_20, - SCREEN_HEIGHT / 2, - SCREEN_HEIGHT / 2 + - 0.75 * SCREEN_UNIT_Y, - "Press START to resume", - 1, - 0, - fg, - bg_s, - &text_prompt_resume); - } - - SDL_RenderPresent(renderer); - } - return 0; -} - -static void* -handler_logic(void *args) -{ - struct timespec clock_start; - struct timespec clock_end; - double clock_pass = 0; - int pipe_dim[6]; - int pipe_pos = 0; - - clock_gettime(CLOCK_MONOTONIC, &clock_start); - - for (;;) { - pthread_mutex_lock(&mutex); - while (start || reset || wait) { - if (!wait) { - clock_pass = 0; - pipe_pos = 0; - } - pthread_cond_wait(&cond, &mutex); - clock_gettime(CLOCK_MONOTONIC, &clock_start); - } - pthread_mutex_unlock(&mutex); - - if (end) - return 0; - - if (flappy) { - pthread_mutex_lock(&mutex); - while (!begin) - pthread_cond_wait(&cond, &mutex); - pthread_mutex_unlock(&mutex); - - game_pipe_bound(&gap, pipe_dim); - - if (pipe_pos - SCREEN_UNIT > pipe_dim[0]) { - score++; - text_score.cache = 0; - } - pipe_pos = pipe_dim[0]; - - if (!god && (collision( - (int[]) {snake0->next->x, snake0->next->y, - 2 * SCREEN_UNIT, SCREEN_UNIT}, - (int[]) {pipe_dim[0], pipe_dim[2], - pipe_dim[1], pipe_dim[3]}) || - collision( - (int[]) {snake0->next->x, snake0->next->y, - 2 * SCREEN_UNIT, SCREEN_UNIT}, - (int[]) {pipe_dim[0], pipe_dim[4], - pipe_dim[1], pipe_dim[5]}) || - snake0->y < 0 || - snake0->y + SCREEN_UNIT > SCREEN_HEIGHT)) { - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - } else { - game_pipe_move(&gap); - - game_pipe_pos(&gap, &result); - if (result == 3) { - game_pipe_del(&gap); - game_pipe_add(&gap); - } else if (result == 2) { - game_pipe_add(&gap); - } else if (result == 1) { - game_pipe_del(&gap); - } - } - } else if (!multiplayer) { - game_snake_move(&snake0, NULL, - head[0], !god, &result); - - if (result == 0 || god) { - head[1][0] = head[0][0]; - - if (collision_block(fruit, - (int[]) {snake0->x, snake0->y})) { - score++; - text_score.cache = 0; - game_snake_grow(&snake0); - - if (score == SCREEN_BLOCK / 2) { - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - } - - game_fruit(fruit, &snake0); - - if (delay.tv_nsec - TICK_TIME_DEC >= - TICK_TIME_MIN) - delay.tv_nsec -= TICK_TIME_DEC; - } - } else { - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - } - } else { - game_snake_move(&snake0, &snake1, - head[0], !god, &result); - - if (result == 0 || god) { - head[1][0] = head[0][0]; - head[1][1] = head[0][1]; - - clock_gettime(CLOCK_MONOTONIC, &clock_end); - clock_pass += (clock_end.tv_sec - - clock_start.tv_sec) + - (clock_end.tv_nsec - - clock_start.tv_nsec) * 0.000000001; - clock_gettime(CLOCK_MONOTONIC, &clock_start); - - score = clock_pass; - text_score.cache = 0; - game_snake_grow(&snake0); - game_snake_grow(&snake1); - } else { - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - } - } - - nanosleep(&delay, NULL); - } -} - -static void* -handler_gravity(void *args) -{ - const struct timespec delay = { - .tv_sec = 0, - .tv_nsec = 1000000000 / SCREEN_UNIT - }; - - for (;;) { - pthread_mutex_lock(&mutex); - while (!flappy || !begin || start || reset || wait) - pthread_cond_wait(&cond, &mutex); - pthread_mutex_unlock(&mutex); - - if (end) - return 0; - - if (distance > 0) { - game_snake_fall(&snake0, 0, - distance > speed ? speed : distance); - speed++; - - pthread_mutex_lock(&mutex); - distance -= distance > speed ? speed : distance; - if (distance == 0) - speed = 1; - pthread_mutex_unlock(&mutex); - } else { - game_snake_fall(&snake0, 1, speed); - speed++; - } - - nanosleep(&delay, NULL); - } -} - -#ifdef GPIO - -#ifdef __FreeBSD__ - -static void* -handler_gpio(void *args) -{ - const struct timespec delay = { - .tv_sec = 0, - .tv_nsec = 50000000 - }; - - SDL_Event event; - event.type = SDL_CONTROLLERBUTTONUP; - - while (!end) { - if (gpio_read(GPIO_PIN_IN0) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_LEFT; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN1) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_UP; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN2) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_DOWN; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN3) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_RIGHT; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN4) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_BACK; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN5) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_GUIDE; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN6) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_START; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN7) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_A; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN8) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_B; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN9) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_X; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN10) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_Y; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN11) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN12) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN13) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_LEFTSTICK; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN14) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_RIGHTSTICK; - event.cbutton.which = 0; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN15) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_LEFT; - event.cbutton.which = 1; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN16) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_UP; - event.cbutton.which = 1; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN17) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_DOWN; - event.cbutton.which = 1; - SDL_PushEvent(&event); - } else if (gpio_read(GPIO_PIN_IN18) == 0) { - event.cbutton.button = - SDL_CONTROLLER_BUTTON_DPAD_RIGHT; - event.cbutton.which = 1; - SDL_PushEvent(&event); - } - - nanosleep(&delay, NULL); - } - return 0; -} - -#elif __linux__ - -static void* -handler_gpio(void *args) -{ - struct pollfd pfd[19]; - char path[29]; - - for (int i = 0; i < 19; i++) - pfd[i].events = POLLPRI | POLLERR; - - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN0); - pfd[0].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN1); - pfd[1].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN2); - pfd[2].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN3); - pfd[3].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN4); - pfd[4].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN5); - pfd[5].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN6); - pfd[6].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN7); - pfd[7].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN8); - pfd[8].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN9); - pfd[9].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN10); - pfd[10].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN11); - pfd[11].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN12); - pfd[12].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN13); - pfd[13].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN14); - pfd[14].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN15); - pfd[15].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN16); - pfd[16].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN17); - pfd[17].fd = open(path, O_RDONLY); - snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN18); - pfd[18].fd = open(path, O_RDONLY); - - SDL_Event event; - event.type = SDL_CONTROLLERBUTTONUP; - - char val[2]; - - while (!end) { - poll(pfd, 19, -1); - - for (int i = 0; i < 19; i++) { - if (pfd[i].revents & (POLLPRI | POLLERR)) { - pfd[i].revents &= ~(POLLPRI | POLLERR); - read(pfd[i].fd, val, 2); - lseek(pfd[i].fd, 0, SEEK_SET); - - if (atoi(val) != 0) - continue; - - input_gpio(i % 15, &event.cbutton.button); - - if (i < 15) - event.cbutton.which = 0; - else - event.cbutton.which = 1; - - SDL_PushEvent(&event); - break; - } - } - } - return 0; -} - -#endif /* __FreeBSD__ */ - -#endif /* GPIO */ - -int -main(void) -{ - printf("Snake " VERSION "." RELEASE "." PATCH EXTRA "\n" - "Copyright (c) 2019-2020, yzrh \n"); - - if (SDL_Init(SDL_INIT_VIDEO | - SDL_INIT_AUDIO | - SDL_INIT_GAMECONTROLLER) != 0) { - fprintf(stderr, "SDL initialisation failure: %s\n", - SDL_GetError()); - return 1; - } - - SDL_Window *window = SDL_CreateWindow( - "Snake " VERSION "." RELEASE "." PATCH EXTRA, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - SCREEN_WIDTH, - SCREEN_HEIGHT, - SDL_WINDOW_OPENGL); - - if (window == NULL) { - fprintf(stderr, "Window creation failure: %s\n", - SDL_GetError()); - return 1; - } - - renderer = SDL_CreateRenderer( - window, - -1, - SDL_RENDERER_ACCELERATED | - SDL_RENDERER_PRESENTVSYNC); - - if (renderer == NULL) { - fprintf(stderr, "Renderer creation failure: %s\n", - SDL_GetError()); - return 1; - } - - if (TTF_Init() != 0) { - fprintf(stderr, "TTF initialisation failure: %s\n", - TTF_GetError()); - return 1; - } - - font_20 = TTF_OpenFont("../contrib/font.ttf", 20); - font_24 = TTF_OpenFont("../contrib/font.ttf", 24); - - if (font_20 == NULL) { - font_20 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 20); - font_24 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 24); - } - - if (font_20 == NULL) { - font_20 = TTF_OpenFont("/usr/share/snake/font.ttf", 20); - font_24 = TTF_OpenFont("/usr/share/snake/font.ttf", 24); - } - - if (font_20 == NULL) { - fprintf(stderr, "%s\n", TTF_GetError()); - return 1; - } - -#ifdef SOUND - - if ((Mix_Init(MIX_INIT_FLAC) & MIX_INIT_FLAC) != MIX_INIT_FLAC) - fprintf(stderr, "Mixer initialisation failure: %s\n", - Mix_GetError()); - - if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 4096) == -1) - fprintf(stderr, "%s\n", Mix_GetError()); - - Mix_Music *music = Mix_LoadMUS("../contrib/music.flac"); - - if (music == NULL) - music = Mix_LoadMUS("/usr/local/share/snake/music.flac"); - - if (music == NULL) - music = Mix_LoadMUS("/usr/share/snake/music.flac"); - - if (music == NULL) - fprintf(stderr, "%s\n", Mix_GetError()); - - if (Mix_PlayMusic(music, -1) == -1) - fprintf(stderr, "Audio playback failure: %s\n", - Mix_GetError()); - -#endif /* SOUND */ - -#ifdef GPIO - -#ifdef __linux__ - - gpio_export(GPIO_PIN_IN0); - gpio_export(GPIO_PIN_IN1); - gpio_export(GPIO_PIN_IN2); - gpio_export(GPIO_PIN_IN3); - gpio_export(GPIO_PIN_IN4); - gpio_export(GPIO_PIN_IN5); - gpio_export(GPIO_PIN_IN6); - gpio_export(GPIO_PIN_IN7); - gpio_export(GPIO_PIN_IN8); - gpio_export(GPIO_PIN_IN9); - gpio_export(GPIO_PIN_IN10); - gpio_export(GPIO_PIN_IN11); - gpio_export(GPIO_PIN_IN12); - gpio_export(GPIO_PIN_IN13); - gpio_export(GPIO_PIN_IN14); - gpio_export(GPIO_PIN_IN15); - gpio_export(GPIO_PIN_IN16); - gpio_export(GPIO_PIN_IN17); - gpio_export(GPIO_PIN_IN18); - - /* Wait for sysfs directory to show up */ - sleep(1); - -#endif /* __linux__ */ - - gpio_config(GPIO_PIN_IN0, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN1, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN2, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN3, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN4, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN5, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN6, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN7, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN8, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN9, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN10, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN11, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN12, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN13, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN14, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN15, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN16, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN17, GPIO_IN, GPIO_INT); - gpio_config(GPIO_PIN_IN18, GPIO_IN, GPIO_INT); - -#endif /* GPIO */ - - int seed; - int random = open("/dev/random", O_RDONLY); - if (read(random, &seed, sizeof(int)) > 0) - srand(seed); - close(random); - - SDL_JoystickID player = 0; - SDL_Event event; - - pthread_mutex_init(&mutex, NULL); - pthread_cond_init(&cond, NULL); - pthread_t th_renderer; - pthread_t th_logic; - pthread_t th_gravity; - - #ifdef GPIO - - pthread_t th_gpio; - - #endif /* GPIO */ - - pthread_create(&th_renderer, NULL, handler_rendering, NULL); - pthread_create(&th_logic, NULL, handler_logic, NULL); - pthread_create(&th_gravity, NULL, handler_gravity, NULL); - - #ifdef GPIO - - pthread_create(&th_gpio, NULL, handler_gpio, NULL); - - #endif /* GPIO */ - - while (SDL_WaitEvent(&event)) { - if (event.type == SDL_QUIT) - break; - - switch (event.type) { - case SDL_KEYUP: - if (device != SNAKE_KEYBOARD) { - device = SNAKE_KEYBOARD; - text_help_1.cache = 0; - text_help_2.cache = 0; - text_help_3.cache = 0; - text_help_4.cache = 0; - text_help_5.cache = 0; - text_help_6.cache = 0; - text_prompt_start.cache = 0; - text_prompt_resume.cache = 0; - } - input_keyboard(event.key.keysym.sym, &input); - if (multiplayer && - (event.key.keysym.sym == - SDLK_s || - event.key.keysym.sym == - SDLK_e || - event.key.keysym.sym == - SDLK_d || - event.key.keysym.sym == - SDLK_f)) - player = 1; - else - player = 0; - break; - case SDL_FINGERUP: - if (device != SNAKE_TOUCHSCREEN) { - device = SNAKE_TOUCHSCREEN; - text_help_1.cache = 0; - text_help_2.cache = 0; - text_help_3.cache = 0; - text_help_4.cache = 0; - text_help_5.cache = 0; - text_help_6.cache = 0; - text_prompt_start.cache = 0; - text_prompt_resume.cache = 0; - } - if (start || reset || wait) { - if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_5)) { - input = SNAKE_RESET; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_6)) { - input = SNAKE_MUSIC; - } else if (wait) { - if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_prompt_resume)) - input = SNAKE_PAUSE; - } else { - if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_prompt_0)) { - selection = 0; - input = SNAKE_SELECT; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_prompt_1)) { - selection = 1; - input = SNAKE_SELECT; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_prompt_2)) { - selection = 2; - input = SNAKE_SELECT; - } - } - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_4)) { - input = SNAKE_PAUSE; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_5)) { - input = SNAKE_RESET; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_6)) { - input = SNAKE_MUSIC; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_top)) { - input = SNAKE_GOD; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_score)) { - input = SNAKE_GROW; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_title)) { - input = SNAKE_TELEPORT; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_1)) { - input = SNAKE_TICK_DECREASE; - } else if (collision_bound( - event.tfinger.x, - event.tfinger.y, - &text_help_7)) { - input = SNAKE_TICK_INCREASE; - } else if (flappy) { - input = SNAKE_JUMP; - } else { - input = SNAKE_EMPTY; - } - break; - case SDL_FINGERMOTION: - if (device != SNAKE_TOUCHSCREEN) { - device = SNAKE_TOUCHSCREEN; - text_help_1.cache = 0; - text_help_2.cache = 0; - text_help_3.cache = 0; - text_help_4.cache = 0; - text_help_5.cache = 0; - text_help_6.cache = 0; - text_prompt_start.cache = 0; - text_prompt_resume.cache = 0; - } - if ((event.tfinger.dx * - event.tfinger.dx) > - (event.tfinger.dy * - event.tfinger.dy)) { - if (event.tfinger.dx > 1) - input = SNAKE_RIGHT; - else if (event.tfinger.dx < -1) - input = SNAKE_LEFT; - } else { - if (event.tfinger.dy > 1) - input = SNAKE_DOWN; - else if (event.tfinger.dy < -1) - input = SNAKE_UP; - } - if (multiplayer && - event.tfinger.x > SCREEN_HEIGHT / 2) - player = 1; - else - player = 0; - break; - case SDL_CONTROLLERBUTTONUP: - if (device != SNAKE_CONTROLLER) { - device = SNAKE_CONTROLLER; - text_help_1.cache = 0; - text_help_2.cache = 0; - text_help_3.cache = 0; - text_help_4.cache = 0; - text_help_5.cache = 0; - text_help_6.cache = 0; - text_prompt_start.cache = 0; - text_prompt_resume.cache = 0; - } - input_controller(event.cbutton.button, &input); - player = event.cbutton.which; - break; - default: - break; - } - - if (!start && !reset && !wait) { - switch (input) { - case SNAKE_LEFT: - if (head[1][player] != SNAKE_RIGHT) - head[0][player] = SNAKE_LEFT; - break; - case SNAKE_UP: - if (head[1][player] != SNAKE_DOWN) - head[0][player] = SNAKE_UP; - break; - case SNAKE_DOWN: - if (head[1][player] != SNAKE_UP) - head[0][player] = SNAKE_DOWN; - break; - case SNAKE_RIGHT: - if (head[1][player] != SNAKE_LEFT) - head[0][player] = SNAKE_RIGHT; - break; - case SNAKE_JUMP: - if (!begin) { - pthread_mutex_lock(&mutex); - begin = 1; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - } - if (flappy) { - pthread_mutex_lock(&mutex); - distance += PIPE_GAP / 2 * - SCREEN_UNIT; - if (distance == 0) - speed = 1; - pthread_mutex_unlock(&mutex); - } - break; - case SNAKE_RESET: - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - break; - case SNAKE_GOD: - god = 1; - break; - case SNAKE_GROW: - if (god && !flappy && !multiplayer) - game_snake_grow(&snake0); - break; - case SNAKE_TELEPORT: - if (god) { - snake0->x = snake0->y = - SCREEN_HEIGHT / 2; - if (flappy) { - snake0->next->x = - SCREEN_HEIGHT / - 2 - SCREEN_UNIT; - snake0->next->y = - SCREEN_HEIGHT / - 2; - } - } - break; - case SNAKE_MUSIC: -#ifdef SOUND - if (Mix_PausedMusic()) - Mix_ResumeMusic(); - else - Mix_PauseMusic(); -#endif /* SOUND */ - break; - case SNAKE_TICK_DECREASE: - if (god && !flappy && delay.tv_nsec - - TICK_TIME_DEC >= TICK_TIME_MIN) - delay.tv_nsec -= TICK_TIME_DEC; - break; - case SNAKE_TICK_INCREASE: - if (god && !flappy) - delay.tv_nsec += TICK_TIME_DEC; - break; - case SNAKE_PAUSE: - wait = 1; - break; - default: - break; - } - } else { - switch (input) { - case SNAKE_RESET: - wait = 0; - - pthread_mutex_lock(&mutex); - reset = 1; - pthread_mutex_unlock(&mutex); - break; - case SNAKE_MUSIC: -#ifdef SOUND - if (Mix_PausedMusic()) - Mix_ResumeMusic(); - else - Mix_PauseMusic(); -#endif /* SOUND */ - break; - case SNAKE_SELECT: - if (wait) - break; - - if (selection == 0) { - flappy = 0; - multiplayer = 0; - } else if (selection == 1) { - flappy = 0; - multiplayer = 1; - } else if (selection == 2) { - multiplayer = 0; - flappy = 1; - } - - delay.tv_nsec = TICK_TIME_INIT; - - god = 0; - - head[0][0] = head[0][1] = - SNAKE_UP; - head[1][0] = head[1][1] = - SNAKE_EMPTY; - - score = 0; - text_score.cache = 0; - - if (flappy) { - delay.tv_nsec = TICK_TIME_INIT / - SCREEN_UNIT; - - game_snake_init(&snake0, 0); - - snake0->next->x = - SCREEN_HEIGHT / 2 - - SCREEN_UNIT; - snake0->next->y = - SCREEN_HEIGHT / 2; - - game_pipe_init(&gap); - - pthread_mutex_lock(&mutex); - begin = 0; - distance = 0; - speed = 1; - pthread_mutex_unlock(&mutex); - } else if (!multiplayer) { - game_snake_init( - &snake0, 0); - game_fruit( - fruit, - &snake0); - } else { - game_snake_init( - &snake0, - -4 * - SCREEN_UNIT); - game_snake_init( - &snake1, - 4 * - SCREEN_UNIT); - } - start = 0; - - pthread_mutex_lock(&mutex); - reset = 0; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - break; - case SNAKE_PAUSE: - wait = 0; - - pthread_mutex_lock(&mutex); - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - break; - case SNAKE_UP: - if ((start || reset) && - selection - 1 >= 0) - selection--; - break; - case SNAKE_DOWN: - if ((start || reset) && - selection + 1 <= 2) - selection++; - break; - default: - break; - } - } - input = SNAKE_EMPTY; - } - - start = 0; - end = 1; - - wait = 0; - flappy = 1; - - pthread_mutex_lock(&mutex); - reset = 0; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - - #ifdef GPIO - - pthread_join(th_gpio, NULL); - - #endif /* GPIO */ - - pthread_join(th_gravity, NULL); - pthread_join(th_logic, NULL); - pthread_join(th_renderer, NULL); - pthread_cond_destroy(&cond); - pthread_mutex_destroy(&mutex); - -#ifdef GPIO - -#ifdef __linux__ - - gpio_unexport(GPIO_PIN_IN0); - gpio_unexport(GPIO_PIN_IN1); - gpio_unexport(GPIO_PIN_IN2); - gpio_unexport(GPIO_PIN_IN3); - gpio_unexport(GPIO_PIN_IN4); - gpio_unexport(GPIO_PIN_IN5); - gpio_unexport(GPIO_PIN_IN6); - gpio_unexport(GPIO_PIN_IN7); - gpio_unexport(GPIO_PIN_IN8); - gpio_unexport(GPIO_PIN_IN9); - gpio_unexport(GPIO_PIN_IN10); - gpio_unexport(GPIO_PIN_IN11); - gpio_unexport(GPIO_PIN_IN12); - gpio_unexport(GPIO_PIN_IN13); - gpio_unexport(GPIO_PIN_IN14); - gpio_unexport(GPIO_PIN_IN15); - gpio_unexport(GPIO_PIN_IN16); - gpio_unexport(GPIO_PIN_IN17); - gpio_unexport(GPIO_PIN_IN18); - -#endif /* __linux__ */ - -#endif /* GPIO */ - -#ifdef SOUND - Mix_Quit(); -#endif /* SOUND */ - - TTF_Quit(); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} diff --git a/src/snake-sdl.c b/src/snake-sdl.c new file mode 100644 index 0000000..48b8fc8 --- /dev/null +++ b/src/snake-sdl.c @@ -0,0 +1,1542 @@ +/* + * Copyright (c) 2019-2020, yzrh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ + +#include +#include +#include + +#endif /* __linux__ */ + +#include +#include + +#ifdef SOUND + +#include + +#endif /* SOUND */ + +#include "version.h" +#include "extern.h" +#include "screen.h" + +#ifdef GPIO + +#include "gpio.h" + +#endif /* GPIO */ + +static pthread_mutex_t mutex; +static pthread_cond_t cond; + +static SDL_Renderer *renderer; +static TTF_Font *font_20; +static TTF_Font *font_24; + +/* + * Default to keyboard, + * GPIO inputs are mapped to controller keys + */ +static Snake_Device device = SNAKE_KEYBOARD; +static int selection = 0; + +static Snake_Text text_title; +static Snake_Text text_help_top; +static Snake_Text text_help_0; +static Snake_Text text_help_1; +static Snake_Text text_help_2; +static Snake_Text text_help_3; +static Snake_Text text_help_4; +static Snake_Text text_help_5; +static Snake_Text text_help_6; +static Snake_Text text_help_7; + +static Snake_Text text_score; + +static Snake_Text text_prompt_start; +static Snake_Text text_prompt_resume; + +static Snake_Text text_prompt_0; +static Snake_Text text_prompt_1; +static Snake_Text text_prompt_2; + +static bool start = 1; +static bool end = 0; +static bool begin = 0; + +static bool god = 0; +static bool reset = 0; +static bool wait = 0; +static bool multiplayer = 0; +static bool flappy = 0; + +static struct timespec delay = { + .tv_sec = 0, + .tv_nsec = TICK_TIME_INIT +}; + +static int score; +static int result; +static int distance; +static int speed; + +static Snake_Input input = SNAKE_EMPTY; +static Snake_Input head[2][2]; + +static Snake_Pos *snake0 = NULL; +static Snake_Pos *snake1 = NULL; + +static int fruit[2]; + +static Snake_Pos *gap = NULL; + +static void* +handler_rendering(void *args) +{ + const SDL_Color fg = { + COLOUR_FOREGROUND_R, + COLOUR_FOREGROUND_G, + COLOUR_FOREGROUND_B + }; + const SDL_Color bg = { + COLOUR_BACKGROUND_R, + COLOUR_BACKGROUND_G, + COLOUR_BACKGROUND_B + }; + const SDL_Color bg_s = { + COLOUR_BACKGROUND_SHADE_R, + COLOUR_BACKGROUND_SHADE_G, + COLOUR_BACKGROUND_SHADE_B + }; + + char result_str[23]; + char score_str[11]; + + Snake_Text text_result; + + Snake_Text text_welcome; + Snake_Text text_clear; + Snake_Text text_gameover; + Snake_Text text_pause; + + while (!end) { + draw_clear(renderer); + draw_colour(renderer, SNAKE_FOREGROUND); + + draw_wall(renderer); + + draw_text(renderer, + font_20, + SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, + SCREEN_UNIT_Y, + "Snake", + 1, + 0, + fg, + bg, + &text_title); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, + 2 * SCREEN_UNIT_Y, + "Help", + 1, + 0, + fg, + bg, + &text_help_top); + if (god) + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 2.5 * SCREEN_UNIT_Y, + "0. Don't forget to cheat", + 0, + 0, + fg, + bg, + &text_help_0); + if (device == SNAKE_KEYBOARD) { + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3 * SCREEN_UNIT_Y, + "1. Classic: use arrow keys to move", + 0, + 0, + fg, + bg, + &text_help_1); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3.5 * SCREEN_UNIT_Y, + "2. Multiplayer: use s, e, d, f keys", + 0, + 0, + fg, + bg, + &text_help_2); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4 * SCREEN_UNIT_Y, + "3. Flappy: SPACE key to jump", + 0, + 0, + fg, + bg, + &text_help_3); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4.5 * SCREEN_UNIT_Y, + "4. ESCAPE key to pause", + 0, + 0, + fg, + bg, + &text_help_4); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5 * SCREEN_UNIT_Y, + "5. r key to reset", + 0, + 0, + fg, + bg, + &text_help_5); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5.5 * SCREEN_UNIT_Y, + "6. m key to toggle music", + 0, + 0, + fg, + bg, + &text_help_6); + } else if (device == SNAKE_TOUCHSCREEN) { + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3 * SCREEN_UNIT_Y, + "1. Classic: swipe to move", + 0, + 0, + fg, + bg, + &text_help_1); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3.5 * SCREEN_UNIT_Y, + "2. Multiplayer: use different side", + 0, + 0, + fg, + bg, + &text_help_2); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4 * SCREEN_UNIT_Y, + "3. Flappy: tap to jump", + 0, + 0, + fg, + bg, + &text_help_3); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4.5 * SCREEN_UNIT_Y, + "4. Tap here to pause", + 0, + 1, + fg, + bg, + &text_help_4); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5 * SCREEN_UNIT_Y, + "5. Tap here to reset", + 0, + 1, + fg, + bg, + &text_help_5); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5.5 * SCREEN_UNIT_Y, + "6. Tap here to toggle music", + 0, + 1, + fg, + bg, + &text_help_6); + } else if (device == SNAKE_CONTROLLER) { + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3 * SCREEN_UNIT_Y, + "1. Classic: use D-pad to move", + 0, + 0, + fg, + bg, + &text_help_1); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 3.5 * SCREEN_UNIT_Y, + "2. Multiplayer: second controller", + 0, + 0, + fg, + bg, + &text_help_2); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4 * SCREEN_UNIT_Y, + "3. Flappy: Press A to jump", + 0, + 0, + fg, + bg, + &text_help_3); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 4.5 * SCREEN_UNIT_Y, + "4. Press BACK to pause", + 0, + 0, + fg, + bg, + &text_help_4); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5 * SCREEN_UNIT_Y, + "5. Press X to reset", + 0, + 0, + fg, + bg, + &text_help_5); + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 5.5 * SCREEN_UNIT_Y, + "6. Press Y to toggle music", + 0, + 0, + fg, + bg, + &text_help_6); + } + draw_text(renderer, + font_20, + SCREEN_HEIGHT + SCREEN_UNIT_X / 4, + 6 * SCREEN_UNIT_Y, + "7. Have fun", + 0, + 0, + fg, + bg, + &text_help_7); + + snprintf(score_str, 11, "Score: %d", score); + + draw_text(renderer, + font_20, + SCREEN_HEIGHT + 4 * SCREEN_UNIT_X, + 7 * SCREEN_UNIT_Y, + score_str, + 1, + 0, + fg, + bg, + &text_score); + + draw_snake(renderer, &snake0); + + if (flappy) { + draw_pipe(renderer, &gap); + } else if (multiplayer) + draw_snake(renderer, &snake1); + else if (!start) + draw_fruit(renderer, fruit); + + if (start || reset) { + if (multiplayer) { + if (result == 3) + snprintf(result_str, 23, "Draw"); + else if (result > 0) + snprintf(result_str, 23, + "%s is the winner", + result == 1 ? + "Player 2" : "Player 1"); + else + snprintf(result_str, 23, "Reset"); + text_result.cache = 0; + + draw_text(renderer, + font_24, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 - + 0.75 * SCREEN_UNIT_Y, + result_str, + 1, + 0, + fg, + bg_s, + &text_result); + } + if (start) + draw_text(renderer, + font_24, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2, + "Welcome", + 1, + 0, + fg, + bg_s, + &text_welcome); + else if (!flappy && !multiplayer && + score == SCREEN_BLOCK / 2) + draw_text(renderer, + font_24, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2, + "Why don't you have legs?", + 1, + 0, + fg, + bg_s, + &text_clear); + else + draw_text(renderer, + font_24, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2, + "Game over!", + 1, + 0, + fg, + bg_s, + &text_gameover); + if (device == SNAKE_KEYBOARD) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Press RETURN key for selection", + 1, + 0, + fg, + bg_s, + &text_prompt_start); + else if (device == SNAKE_TOUCHSCREEN) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Tap the option to select", + 1, + 0, + fg, + bg_s, + &text_prompt_start); + else if (device == SNAKE_CONTROLLER) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Press START to select", + 1, + 0, + fg, + bg_s, + &text_prompt_start); + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + 1.5 * SCREEN_UNIT_Y, + "Classic", + 1, + device == SNAKE_TOUCHSCREEN || + selection == 0 ? 1 : 0, + fg, + bg_s, + &text_prompt_0); + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + 2.25 * SCREEN_UNIT_Y, + "Multiplayer", + 1, + device == SNAKE_TOUCHSCREEN || + selection == 1 ? 1 : 0, + fg, + bg_s, + &text_prompt_1); + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + 3 * SCREEN_UNIT_Y, + "Flappy", + 1, + device == SNAKE_TOUCHSCREEN || + selection == 2 ? 1 : 0, + fg, + bg_s, + &text_prompt_2); + } else if (wait) { + draw_text(renderer, + font_24, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2, + "Paused", + 1, + 0, + fg, + bg_s, + &text_pause); + if (device == SNAKE_KEYBOARD) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Press ESCAPE key to resume", + 1, + 0, + fg, + bg_s, + &text_prompt_resume); + else if (device == SNAKE_TOUCHSCREEN) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Tap here to resume", + 1, + 1, + fg, + bg_s, + &text_prompt_resume); + else if (device == SNAKE_CONTROLLER) + draw_text(renderer, + font_20, + SCREEN_HEIGHT / 2, + SCREEN_HEIGHT / 2 + + 0.75 * SCREEN_UNIT_Y, + "Press START to resume", + 1, + 0, + fg, + bg_s, + &text_prompt_resume); + } + + SDL_RenderPresent(renderer); + } + return 0; +} + +static void* +handler_logic(void *args) +{ + struct timespec clock_start; + struct timespec clock_end; + double clock_pass = 0; + int pipe_dim[6]; + int pipe_pos = 0; + + clock_gettime(CLOCK_MONOTONIC, &clock_start); + + for (;;) { + pthread_mutex_lock(&mutex); + while (start || reset || wait) { + if (!wait) { + clock_pass = 0; + pipe_pos = 0; + } + pthread_cond_wait(&cond, &mutex); + clock_gettime(CLOCK_MONOTONIC, &clock_start); + } + pthread_mutex_unlock(&mutex); + + if (end) + return 0; + + if (flappy) { + pthread_mutex_lock(&mutex); + while (!begin) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + + game_pipe_bound(&gap, pipe_dim); + + if (pipe_pos - SCREEN_UNIT > pipe_dim[0]) { + score++; + text_score.cache = 0; + } + pipe_pos = pipe_dim[0]; + + if (!god && (collision( + (int[]) {snake0->next->x, snake0->next->y, + 2 * SCREEN_UNIT, SCREEN_UNIT}, + (int[]) {pipe_dim[0], pipe_dim[2], + pipe_dim[1], pipe_dim[3]}) || + collision( + (int[]) {snake0->next->x, snake0->next->y, + 2 * SCREEN_UNIT, SCREEN_UNIT}, + (int[]) {pipe_dim[0], pipe_dim[4], + pipe_dim[1], pipe_dim[5]}) || + snake0->y < 0 || + snake0->y + SCREEN_UNIT > SCREEN_HEIGHT)) { + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + } else { + game_pipe_move(&gap); + + game_pipe_pos(&gap, &result); + if (result == 3) { + game_pipe_del(&gap); + game_pipe_add(&gap); + } else if (result == 2) { + game_pipe_add(&gap); + } else if (result == 1) { + game_pipe_del(&gap); + } + } + } else if (!multiplayer) { + game_snake_move(&snake0, NULL, + head[0], !god, &result); + + if (result == 0 || god) { + head[1][0] = head[0][0]; + + if (collision_block(fruit, + (int[]) {snake0->x, snake0->y})) { + score++; + text_score.cache = 0; + game_snake_grow(&snake0); + + if (score == SCREEN_BLOCK / 2) { + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + } + + game_fruit(fruit, &snake0); + + if (delay.tv_nsec - TICK_TIME_DEC >= + TICK_TIME_MIN) + delay.tv_nsec -= TICK_TIME_DEC; + } + } else { + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + } + } else { + game_snake_move(&snake0, &snake1, + head[0], !god, &result); + + if (result == 0 || god) { + head[1][0] = head[0][0]; + head[1][1] = head[0][1]; + + clock_gettime(CLOCK_MONOTONIC, &clock_end); + clock_pass += (clock_end.tv_sec - + clock_start.tv_sec) + + (clock_end.tv_nsec - + clock_start.tv_nsec) * 0.000000001; + clock_gettime(CLOCK_MONOTONIC, &clock_start); + + score = clock_pass; + text_score.cache = 0; + game_snake_grow(&snake0); + game_snake_grow(&snake1); + } else { + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + } + } + + nanosleep(&delay, NULL); + } +} + +static void* +handler_gravity(void *args) +{ + const struct timespec delay = { + .tv_sec = 0, + .tv_nsec = 1000000000 / SCREEN_UNIT + }; + + for (;;) { + pthread_mutex_lock(&mutex); + while (!flappy || !begin || start || reset || wait) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + + if (end) + return 0; + + if (distance > 0) { + game_snake_fall(&snake0, 0, + distance > speed ? speed : distance); + speed++; + + pthread_mutex_lock(&mutex); + distance -= distance > speed ? speed : distance; + if (distance == 0) + speed = 1; + pthread_mutex_unlock(&mutex); + } else { + game_snake_fall(&snake0, 1, speed); + speed++; + } + + nanosleep(&delay, NULL); + } +} + +#ifdef GPIO + +#ifdef __FreeBSD__ + +static void* +handler_gpio(void *args) +{ + const struct timespec delay = { + .tv_sec = 0, + .tv_nsec = 50000000 + }; + + SDL_Event event; + event.type = SDL_CONTROLLERBUTTONUP; + + while (!end) { + if (gpio_read(GPIO_PIN_IN0) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_LEFT; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN1) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_UP; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN2) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_DOWN; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN3) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_RIGHT; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN4) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_BACK; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN5) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_GUIDE; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN6) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_START; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN7) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_A; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN8) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_B; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN9) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_X; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN10) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_Y; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN11) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN12) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN13) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_LEFTSTICK; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN14) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_RIGHTSTICK; + event.cbutton.which = 0; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN15) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_LEFT; + event.cbutton.which = 1; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN16) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_UP; + event.cbutton.which = 1; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN17) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_DOWN; + event.cbutton.which = 1; + SDL_PushEvent(&event); + } else if (gpio_read(GPIO_PIN_IN18) == 0) { + event.cbutton.button = + SDL_CONTROLLER_BUTTON_DPAD_RIGHT; + event.cbutton.which = 1; + SDL_PushEvent(&event); + } + + nanosleep(&delay, NULL); + } + return 0; +} + +#elif __linux__ + +static void* +handler_gpio(void *args) +{ + struct pollfd pfd[19]; + char path[29]; + + for (int i = 0; i < 19; i++) + pfd[i].events = POLLPRI | POLLERR; + + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN0); + pfd[0].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN1); + pfd[1].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN2); + pfd[2].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN3); + pfd[3].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN4); + pfd[4].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN5); + pfd[5].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN6); + pfd[6].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN7); + pfd[7].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN8); + pfd[8].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN9); + pfd[9].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN10); + pfd[10].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN11); + pfd[11].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN12); + pfd[12].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN13); + pfd[13].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN14); + pfd[14].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN15); + pfd[15].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN16); + pfd[16].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN17); + pfd[17].fd = open(path, O_RDONLY); + snprintf(path, 29, "/sys/class/gpio/gpio%d/value", GPIO_PIN_IN18); + pfd[18].fd = open(path, O_RDONLY); + + SDL_Event event; + event.type = SDL_CONTROLLERBUTTONUP; + + char val[2]; + + while (!end) { + poll(pfd, 19, -1); + + for (int i = 0; i < 19; i++) { + if (pfd[i].revents & (POLLPRI | POLLERR)) { + pfd[i].revents &= ~(POLLPRI | POLLERR); + read(pfd[i].fd, val, 2); + lseek(pfd[i].fd, 0, SEEK_SET); + + if (atoi(val) != 0) + continue; + + input_gpio(i % 15, &event.cbutton.button); + + if (i < 15) + event.cbutton.which = 0; + else + event.cbutton.which = 1; + + SDL_PushEvent(&event); + break; + } + } + } + return 0; +} + +#endif /* __FreeBSD__ */ + +#endif /* GPIO */ + +int +main(void) +{ + printf("Snake " VERSION "." RELEASE "." PATCH EXTRA "\n" + "Copyright (c) 2019-2020, yzrh \n"); + + if (SDL_Init(SDL_INIT_VIDEO | + SDL_INIT_AUDIO | + SDL_INIT_GAMECONTROLLER) != 0) { + fprintf(stderr, "SDL initialisation failure: %s\n", + SDL_GetError()); + return 1; + } + + SDL_Window *window = SDL_CreateWindow( + "Snake " VERSION "." RELEASE "." PATCH EXTRA, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + SCREEN_WIDTH, + SCREEN_HEIGHT, + SDL_WINDOW_OPENGL); + + if (window == NULL) { + fprintf(stderr, "Window creation failure: %s\n", + SDL_GetError()); + return 1; + } + + renderer = SDL_CreateRenderer( + window, + -1, + SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC); + + if (renderer == NULL) { + fprintf(stderr, "Renderer creation failure: %s\n", + SDL_GetError()); + return 1; + } + + if (TTF_Init() != 0) { + fprintf(stderr, "TTF initialisation failure: %s\n", + TTF_GetError()); + return 1; + } + + font_20 = TTF_OpenFont("../contrib/font.ttf", 20); + font_24 = TTF_OpenFont("../contrib/font.ttf", 24); + + if (font_20 == NULL) { + font_20 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 20); + font_24 = TTF_OpenFont("/usr/local/share/snake/font.ttf", 24); + } + + if (font_20 == NULL) { + font_20 = TTF_OpenFont("/usr/share/snake/font.ttf", 20); + font_24 = TTF_OpenFont("/usr/share/snake/font.ttf", 24); + } + + if (font_20 == NULL) { + fprintf(stderr, "%s\n", TTF_GetError()); + return 1; + } + +#ifdef SOUND + + if ((Mix_Init(MIX_INIT_FLAC) & MIX_INIT_FLAC) != MIX_INIT_FLAC) + fprintf(stderr, "Mixer initialisation failure: %s\n", + Mix_GetError()); + + if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 4096) == -1) + fprintf(stderr, "%s\n", Mix_GetError()); + + Mix_Music *music = Mix_LoadMUS("../contrib/music.flac"); + + if (music == NULL) + music = Mix_LoadMUS("/usr/local/share/snake/music.flac"); + + if (music == NULL) + music = Mix_LoadMUS("/usr/share/snake/music.flac"); + + if (music == NULL) + fprintf(stderr, "%s\n", Mix_GetError()); + + if (Mix_PlayMusic(music, -1) == -1) + fprintf(stderr, "Audio playback failure: %s\n", + Mix_GetError()); + +#endif /* SOUND */ + +#ifdef GPIO + +#ifdef __linux__ + + gpio_export(GPIO_PIN_IN0); + gpio_export(GPIO_PIN_IN1); + gpio_export(GPIO_PIN_IN2); + gpio_export(GPIO_PIN_IN3); + gpio_export(GPIO_PIN_IN4); + gpio_export(GPIO_PIN_IN5); + gpio_export(GPIO_PIN_IN6); + gpio_export(GPIO_PIN_IN7); + gpio_export(GPIO_PIN_IN8); + gpio_export(GPIO_PIN_IN9); + gpio_export(GPIO_PIN_IN10); + gpio_export(GPIO_PIN_IN11); + gpio_export(GPIO_PIN_IN12); + gpio_export(GPIO_PIN_IN13); + gpio_export(GPIO_PIN_IN14); + gpio_export(GPIO_PIN_IN15); + gpio_export(GPIO_PIN_IN16); + gpio_export(GPIO_PIN_IN17); + gpio_export(GPIO_PIN_IN18); + + /* Wait for sysfs directory to show up */ + sleep(1); + +#endif /* __linux__ */ + + gpio_config(GPIO_PIN_IN0, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN1, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN2, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN3, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN4, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN5, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN6, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN7, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN8, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN9, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN10, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN11, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN12, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN13, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN14, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN15, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN16, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN17, GPIO_IN, GPIO_INT); + gpio_config(GPIO_PIN_IN18, GPIO_IN, GPIO_INT); + +#endif /* GPIO */ + + int seed; + int random = open("/dev/random", O_RDONLY); + if (read(random, &seed, sizeof(int)) > 0) + srand(seed); + close(random); + + SDL_JoystickID player = 0; + SDL_Event event; + + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); + pthread_t th_renderer; + pthread_t th_logic; + pthread_t th_gravity; + + #ifdef GPIO + + pthread_t th_gpio; + + #endif /* GPIO */ + + pthread_create(&th_renderer, NULL, handler_rendering, NULL); + pthread_create(&th_logic, NULL, handler_logic, NULL); + pthread_create(&th_gravity, NULL, handler_gravity, NULL); + + #ifdef GPIO + + pthread_create(&th_gpio, NULL, handler_gpio, NULL); + + #endif /* GPIO */ + + while (SDL_WaitEvent(&event)) { + if (event.type == SDL_QUIT) + break; + + switch (event.type) { + case SDL_KEYUP: + if (device != SNAKE_KEYBOARD) { + device = SNAKE_KEYBOARD; + text_help_1.cache = 0; + text_help_2.cache = 0; + text_help_3.cache = 0; + text_help_4.cache = 0; + text_help_5.cache = 0; + text_help_6.cache = 0; + text_prompt_start.cache = 0; + text_prompt_resume.cache = 0; + } + input_keyboard(event.key.keysym.sym, &input); + if (multiplayer && + (event.key.keysym.sym == + SDLK_s || + event.key.keysym.sym == + SDLK_e || + event.key.keysym.sym == + SDLK_d || + event.key.keysym.sym == + SDLK_f)) + player = 1; + else + player = 0; + break; + case SDL_FINGERUP: + if (device != SNAKE_TOUCHSCREEN) { + device = SNAKE_TOUCHSCREEN; + text_help_1.cache = 0; + text_help_2.cache = 0; + text_help_3.cache = 0; + text_help_4.cache = 0; + text_help_5.cache = 0; + text_help_6.cache = 0; + text_prompt_start.cache = 0; + text_prompt_resume.cache = 0; + } + if (start || reset || wait) { + if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_5)) { + input = SNAKE_RESET; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_6)) { + input = SNAKE_MUSIC; + } else if (wait) { + if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_prompt_resume)) + input = SNAKE_PAUSE; + } else { + if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_prompt_0)) { + selection = 0; + input = SNAKE_SELECT; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_prompt_1)) { + selection = 1; + input = SNAKE_SELECT; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_prompt_2)) { + selection = 2; + input = SNAKE_SELECT; + } + } + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_4)) { + input = SNAKE_PAUSE; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_5)) { + input = SNAKE_RESET; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_6)) { + input = SNAKE_MUSIC; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_top)) { + input = SNAKE_GOD; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_score)) { + input = SNAKE_GROW; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_title)) { + input = SNAKE_TELEPORT; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_1)) { + input = SNAKE_TICK_DECREASE; + } else if (collision_bound( + event.tfinger.x, + event.tfinger.y, + &text_help_7)) { + input = SNAKE_TICK_INCREASE; + } else if (flappy) { + input = SNAKE_JUMP; + } else { + input = SNAKE_EMPTY; + } + break; + case SDL_FINGERMOTION: + if (device != SNAKE_TOUCHSCREEN) { + device = SNAKE_TOUCHSCREEN; + text_help_1.cache = 0; + text_help_2.cache = 0; + text_help_3.cache = 0; + text_help_4.cache = 0; + text_help_5.cache = 0; + text_help_6.cache = 0; + text_prompt_start.cache = 0; + text_prompt_resume.cache = 0; + } + if ((event.tfinger.dx * + event.tfinger.dx) > + (event.tfinger.dy * + event.tfinger.dy)) { + if (event.tfinger.dx > 1) + input = SNAKE_RIGHT; + else if (event.tfinger.dx < -1) + input = SNAKE_LEFT; + } else { + if (event.tfinger.dy > 1) + input = SNAKE_DOWN; + else if (event.tfinger.dy < -1) + input = SNAKE_UP; + } + if (multiplayer && + event.tfinger.x > SCREEN_HEIGHT / 2) + player = 1; + else + player = 0; + break; + case SDL_CONTROLLERBUTTONUP: + if (device != SNAKE_CONTROLLER) { + device = SNAKE_CONTROLLER; + text_help_1.cache = 0; + text_help_2.cache = 0; + text_help_3.cache = 0; + text_help_4.cache = 0; + text_help_5.cache = 0; + text_help_6.cache = 0; + text_prompt_start.cache = 0; + text_prompt_resume.cache = 0; + } + input_controller(event.cbutton.button, &input); + player = event.cbutton.which; + break; + default: + break; + } + + if (!start && !reset && !wait) { + switch (input) { + case SNAKE_LEFT: + if (head[1][player] != SNAKE_RIGHT) + head[0][player] = SNAKE_LEFT; + break; + case SNAKE_UP: + if (head[1][player] != SNAKE_DOWN) + head[0][player] = SNAKE_UP; + break; + case SNAKE_DOWN: + if (head[1][player] != SNAKE_UP) + head[0][player] = SNAKE_DOWN; + break; + case SNAKE_RIGHT: + if (head[1][player] != SNAKE_LEFT) + head[0][player] = SNAKE_RIGHT; + break; + case SNAKE_JUMP: + if (!begin) { + pthread_mutex_lock(&mutex); + begin = 1; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + } + if (flappy) { + pthread_mutex_lock(&mutex); + distance += PIPE_GAP / 2 * + SCREEN_UNIT; + if (distance == 0) + speed = 1; + pthread_mutex_unlock(&mutex); + } + break; + case SNAKE_RESET: + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + break; + case SNAKE_GOD: + god = 1; + break; + case SNAKE_GROW: + if (god && !flappy && !multiplayer) + game_snake_grow(&snake0); + break; + case SNAKE_TELEPORT: + if (god) { + snake0->x = snake0->y = + SCREEN_HEIGHT / 2; + if (flappy) { + snake0->next->x = + SCREEN_HEIGHT / + 2 - SCREEN_UNIT; + snake0->next->y = + SCREEN_HEIGHT / + 2; + } + } + break; + case SNAKE_MUSIC: +#ifdef SOUND + if (Mix_PausedMusic()) + Mix_ResumeMusic(); + else + Mix_PauseMusic(); +#endif /* SOUND */ + break; + case SNAKE_TICK_DECREASE: + if (god && !flappy && delay.tv_nsec - + TICK_TIME_DEC >= TICK_TIME_MIN) + delay.tv_nsec -= TICK_TIME_DEC; + break; + case SNAKE_TICK_INCREASE: + if (god && !flappy) + delay.tv_nsec += TICK_TIME_DEC; + break; + case SNAKE_PAUSE: + wait = 1; + break; + default: + break; + } + } else { + switch (input) { + case SNAKE_RESET: + wait = 0; + + pthread_mutex_lock(&mutex); + reset = 1; + pthread_mutex_unlock(&mutex); + break; + case SNAKE_MUSIC: +#ifdef SOUND + if (Mix_PausedMusic()) + Mix_ResumeMusic(); + else + Mix_PauseMusic(); +#endif /* SOUND */ + break; + case SNAKE_SELECT: + if (wait) + break; + + if (selection == 0) { + flappy = 0; + multiplayer = 0; + } else if (selection == 1) { + flappy = 0; + multiplayer = 1; + } else if (selection == 2) { + multiplayer = 0; + flappy = 1; + } + + delay.tv_nsec = TICK_TIME_INIT; + + god = 0; + + head[0][0] = head[0][1] = + SNAKE_UP; + head[1][0] = head[1][1] = + SNAKE_EMPTY; + + score = 0; + text_score.cache = 0; + + if (flappy) { + delay.tv_nsec = TICK_TIME_INIT / + SCREEN_UNIT; + + game_snake_init(&snake0, 0); + + snake0->next->x = + SCREEN_HEIGHT / 2 - + SCREEN_UNIT; + snake0->next->y = + SCREEN_HEIGHT / 2; + + game_pipe_init(&gap); + + pthread_mutex_lock(&mutex); + begin = 0; + distance = 0; + speed = 1; + pthread_mutex_unlock(&mutex); + } else if (!multiplayer) { + game_snake_init( + &snake0, 0); + game_fruit( + fruit, + &snake0); + } else { + game_snake_init( + &snake0, + -4 * + SCREEN_UNIT); + game_snake_init( + &snake1, + 4 * + SCREEN_UNIT); + } + start = 0; + + pthread_mutex_lock(&mutex); + reset = 0; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + break; + case SNAKE_PAUSE: + wait = 0; + + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + break; + case SNAKE_UP: + if ((start || reset) && + selection - 1 >= 0) + selection--; + break; + case SNAKE_DOWN: + if ((start || reset) && + selection + 1 <= 2) + selection++; + break; + default: + break; + } + } + input = SNAKE_EMPTY; + } + + start = 0; + end = 1; + + wait = 0; + flappy = 1; + + pthread_mutex_lock(&mutex); + reset = 0; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + + #ifdef GPIO + + pthread_join(th_gpio, NULL); + + #endif /* GPIO */ + + pthread_join(th_gravity, NULL); + pthread_join(th_logic, NULL); + pthread_join(th_renderer, NULL); + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); + +#ifdef GPIO + +#ifdef __linux__ + + gpio_unexport(GPIO_PIN_IN0); + gpio_unexport(GPIO_PIN_IN1); + gpio_unexport(GPIO_PIN_IN2); + gpio_unexport(GPIO_PIN_IN3); + gpio_unexport(GPIO_PIN_IN4); + gpio_unexport(GPIO_PIN_IN5); + gpio_unexport(GPIO_PIN_IN6); + gpio_unexport(GPIO_PIN_IN7); + gpio_unexport(GPIO_PIN_IN8); + gpio_unexport(GPIO_PIN_IN9); + gpio_unexport(GPIO_PIN_IN10); + gpio_unexport(GPIO_PIN_IN11); + gpio_unexport(GPIO_PIN_IN12); + gpio_unexport(GPIO_PIN_IN13); + gpio_unexport(GPIO_PIN_IN14); + gpio_unexport(GPIO_PIN_IN15); + gpio_unexport(GPIO_PIN_IN16); + gpio_unexport(GPIO_PIN_IN17); + gpio_unexport(GPIO_PIN_IN18); + +#endif /* __linux__ */ + +#endif /* GPIO */ + +#ifdef SOUND + Mix_Quit(); +#endif /* SOUND */ + + TTF_Quit(); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} diff --git a/src/version.h b/src/version.h index cd17685..874129a 100644 --- a/src/version.h +++ b/src/version.h @@ -7,4 +7,4 @@ #define VERSION "0" #define RELEASE "6" #define PATCH "2" -#define EXTRA "_4" +#define EXTRA "_5" -- cgit v1.2.3