mirror of
https://github.com/thepigeongenerator/tetris_clone.git
synced 2025-12-17 05:55:46 +01:00
full removal of SDL
This commit is contained in:
1
res/sh.frag.glsl
Normal file
1
res/sh.frag.glsl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#version 330 core
|
||||||
1
res/sh.vert.glsl
Normal file
1
res/sh.vert.glsl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#version 330 core
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#include "game.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "../io/colour/colour8.h"
|
|
||||||
#include "../io/input.h"
|
|
||||||
#include "../io/window.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "./tetromino/shapes.h"
|
|
||||||
#include "tetromino/placing.h"
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
static colour8 rowdat[COLUMNS * ROWS] = {0}; // contains the raw data of the rows, in no particular order
|
|
||||||
static struct gamedata dat = {0};
|
|
||||||
|
|
||||||
/* shuffle an array using the Fisher–Yates shuffle algorithm.
|
|
||||||
* `nmemb` is the number of members.
|
|
||||||
* `membs` is the byte size of each member */
|
|
||||||
static void shuffle(void *restrict ptr, size_t nmemb, size_t membs) {
|
|
||||||
u8 dat[membs];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nmemb; i++) {
|
|
||||||
size_t j = i + rand() % (nmemb - i);
|
|
||||||
void *ptri = (u8 *)ptr + i * membs;
|
|
||||||
void *ptrj = (u8 *)ptr + j * membs;
|
|
||||||
memcpy(dat, ptri, membs);
|
|
||||||
memcpy(ptri, ptrj, membs);
|
|
||||||
memcpy(ptrj, dat, membs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void next_shape(void) {
|
|
||||||
// as long as we're not at the last shape, we can just increment
|
|
||||||
dat.pdat.sel = (i8vec2){COLUMNS / 2 - SHAPE_WIDTH / 2, 0};
|
|
||||||
dat.pdat.cur = dat.pdat.nxt[dat.pdat.idx];
|
|
||||||
dat.pdat.idx++;
|
|
||||||
if (dat.pdat.idx < TETC) return;
|
|
||||||
|
|
||||||
// shuffle all next shapes, preserving the last
|
|
||||||
dat.pdat.idx = 0;
|
|
||||||
shuffle(dat.pdat.nxt, TETC, sizeof(u8));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gamedata *game_init(void) {
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
// populate the data arrays
|
|
||||||
for (int i = 0; i < ROWS; i++)
|
|
||||||
dat.rows[i] = rowdat + i * COLUMNS;
|
|
||||||
for (int i = 0; i < TETC; i++)
|
|
||||||
dat.pdat.nxt[i] = i;
|
|
||||||
|
|
||||||
// initialise the placing data correctly
|
|
||||||
dat.pdat.sel = (i8vec2){COLUMNS / 2 - SHAPE_WIDTH / 2, 0};
|
|
||||||
shuffle(dat.pdat.nxt, TETC, sizeof(u8));
|
|
||||||
dat.pdat.cur = dat.pdat.nxt[dat.pdat.idx];
|
|
||||||
dat.pdat.idx++;
|
|
||||||
return &dat;
|
|
||||||
}
|
|
||||||
|
|
||||||
// called every time the game's state is updated
|
|
||||||
void game_update(int movdat, size_t time) {
|
|
||||||
static time_t drop_timeout = 0;
|
|
||||||
movdat |= MOVD & -!!time_poll(time, 200, &drop_timeout);
|
|
||||||
|
|
||||||
if (place_update(&dat, movdat))
|
|
||||||
window_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_free(void) {
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "../io/colour/colour8.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "tetromino/shapes.h"
|
|
||||||
|
|
||||||
// constants for pi(π) and tau(τ)
|
|
||||||
#define PI (M_PI) // π constant
|
|
||||||
#define TAU (M_PI * 2.0) // τ constant
|
|
||||||
#define PIf (M_PIf) // π constant as a 32-bit floating point
|
|
||||||
#define TAUf (M_PIf * 2.0F) // τ constant as a 32-bit floating point
|
|
||||||
|
|
||||||
// stores the data used in the game
|
|
||||||
#define COLUMNS 10
|
|
||||||
#define ROWS 24
|
|
||||||
|
|
||||||
/* contains the placement data */
|
|
||||||
struct pdat {
|
|
||||||
u8 nxt[TETC]; // shuffled data representing the next shapes
|
|
||||||
i8vec2 sel; // position of the current shape
|
|
||||||
u8 idx; // the index of the current shape
|
|
||||||
u8 cur; // the current id of the shape
|
|
||||||
};
|
|
||||||
|
|
||||||
/* contains game data that's commonly shared */
|
|
||||||
struct gamedata {
|
|
||||||
struct pdat pdat;
|
|
||||||
colour8 *rows[ROWS];
|
|
||||||
u16 pnts;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* increments to the next shape, shuffling the next shapes, if there isn't a next shape immediately after the current one. */
|
|
||||||
void next_shape(void);
|
|
||||||
|
|
||||||
struct gamedata *game_init(void);
|
|
||||||
void game_update(int movdat, size_t time);
|
|
||||||
void game_free(void);
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#include "time.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#if __has_include(<features.h>)
|
|
||||||
#include <features.h>
|
|
||||||
#endif
|
|
||||||
#if __has_include(<features.h>) && _POSIX_C_SOURCE >= 199309L
|
|
||||||
#include <bits/time.h>
|
|
||||||
static void gettime(struct timespec *ts) {
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, ts);
|
|
||||||
}
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <profileapi.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <winnt.h>
|
|
||||||
static void gettime(struct timespec *ts) {
|
|
||||||
LARGE_INTEGER cnt, frq;
|
|
||||||
QueryPerformanceCounter(&cnt);
|
|
||||||
QueryPerformanceFrequency(&frq);
|
|
||||||
ts->tv_sec = (time_t)(cnt.QuadPart / frq.QuadPart);
|
|
||||||
ts->tv_nsec = (time_t)((cnt.QuadPart % frq.QuadPart) * 1000000000 / frq.QuadPart);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error no implementation of a monotonic clock was available
|
|
||||||
#endif
|
|
||||||
|
|
||||||
time_t time_pull(void) {
|
|
||||||
struct timespec ts;
|
|
||||||
gettime(&ts);
|
|
||||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int time_poll(time_t curr, time_t delta, time_t *restrict proj) {
|
|
||||||
bool tpass = curr >= *proj;
|
|
||||||
*proj += tpass * ((curr + delta) - *proj); // adds 0, or the difference to proj
|
|
||||||
return tpass;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
/* gets the current time in milliseconds */
|
|
||||||
time_t time_pull(void);
|
|
||||||
|
|
||||||
/* Polls the time whether a given timeout has passed, comparing against `curr` as the current time.
|
|
||||||
* if `curr` ≥ `*proj`, `curr` + `delta` is written to `*proj`. `1` is returned.
|
|
||||||
* otherwise, we just return `0`. */
|
|
||||||
__nonnull((3)) int time_poll(time_t curr, time_t delta, time_t *restrict proj);
|
|
||||||
150
src/io/audio.c
150
src/io/audio.c
@@ -1,150 +0,0 @@
|
|||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <SDL_error.h>
|
|
||||||
#include <SDL_stdinc.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "../error.h"
|
|
||||||
#include "../util/compat.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
|
|
||||||
struct audioplayer {
|
|
||||||
const u8 *buf;
|
|
||||||
int len;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audiodevice {
|
|
||||||
struct audioplayer audio[AUDIO_MAX];
|
|
||||||
u32 id;
|
|
||||||
SDL_AudioSpec spec;
|
|
||||||
} dev;
|
|
||||||
|
|
||||||
struct audiodata audio_dat[AUDIO_ID_COUNT] = {0}; // contains pointers to audio buffers.
|
|
||||||
|
|
||||||
static const char *const audio_path[AUDIO_ID_COUNT] = {
|
|
||||||
"korobeiniki.wav",
|
|
||||||
"place.wav",
|
|
||||||
};
|
|
||||||
|
|
||||||
/* mixes the audio output stream, using the different audio as sources */
|
|
||||||
static void audiomixer(void *const userdata, u8 *const stream, const int len) {
|
|
||||||
(void)userdata;
|
|
||||||
memset(stream, 0, len); // clear the playing audio
|
|
||||||
|
|
||||||
// update the counts for the audio array
|
|
||||||
for (unsigned i = 0; i < AUDIO_MAX; i++) {
|
|
||||||
if (dev.audio[i].len > 0) {
|
|
||||||
unsigned mixlen = SDL_min(len, dev.audio[i].len);
|
|
||||||
SDL_MixAudioFormat(stream, dev.audio[i].buf, dev.spec.format, mixlen, SDL_MIX_MAXVOLUME);
|
|
||||||
dev.audio[i].len -= mixlen;
|
|
||||||
dev.audio[i].buf += mixlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* converts input audio buffer to one that matches the buffer of dev.
|
|
||||||
* `len` is a pointer to the current size, the new size will be written to this location.
|
|
||||||
* returns the pointer to the audio buffer to use, or NULL, when something went wrong.
|
|
||||||
* NULL will never be returned after the conversion */
|
|
||||||
static u8 *audio_cvt(const SDL_AudioSpec *spec, u8 *bufptr, unsigned *len) {
|
|
||||||
if (!bufptr) return NULL;
|
|
||||||
|
|
||||||
// init the converter
|
|
||||||
SDL_AudioCVT cvt;
|
|
||||||
if (SDL_BuildAudioCVT(&cvt, spec->format, spec->channels, spec->freq, dev.spec.format, dev.spec.channels, dev.spec.freq) < 0) {
|
|
||||||
error("could not build the audio converter! SDL Error: %s", SDL_GetError());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!cvt.needed) return bufptr; // ensure the conversion is necessary
|
|
||||||
|
|
||||||
// grow the inputted buffer for the conversion
|
|
||||||
cvt.len = *len;
|
|
||||||
cvt.buf = realloc(bufptr, cvt.len * cvt.len_mult);
|
|
||||||
if (!cvt.buf) {
|
|
||||||
warn("failed to grow the audio buffer to the new size of %u bytes! audio may be bugged", cvt.len);
|
|
||||||
return bufptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the audio to the new format
|
|
||||||
if (SDL_ConvertAudio(&cvt) < 0) {
|
|
||||||
warn("couldn't convert an audio buffer to the set format, audio may be bugged! SDL Error: %s", SDL_GetError());
|
|
||||||
assert(cvt.buf);
|
|
||||||
return cvt.buf;
|
|
||||||
}
|
|
||||||
assert(cvt.buf);
|
|
||||||
|
|
||||||
// shrink to reduce memory footprint
|
|
||||||
*len = cvt.len_cvt;
|
|
||||||
bufptr = realloc(cvt.buf, *len);
|
|
||||||
return bufptr ? bufptr : cvt.buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* computes the time in milliseconds of the audio fragment by dividing
|
|
||||||
* the audio byte length by the format's bit size, which is divided by
|
|
||||||
* the audio device's channels and frequency */
|
|
||||||
static inline u32 audio_btoms(u32 len) {
|
|
||||||
return (((1000 * len) / (SDL_AUDIO_BITSIZE(dev.spec.format) / 8)) / dev.spec.channels / dev.spec.freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loads a `struct audiodata` from `fpat` to `out`. */
|
|
||||||
static void audio_wav_load(const char *restrict fpat, struct audiodata *restrict out) {
|
|
||||||
debug("loading audio file '%s'...", fpat);
|
|
||||||
if (faccess(fpat, FA_R)) {
|
|
||||||
error("audio file either isn't readable or doesn't exist. path: '%s'!", fpat);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the audio
|
|
||||||
u32 len;
|
|
||||||
u8 *ptr, *tmp;
|
|
||||||
SDL_AudioSpec spec;
|
|
||||||
SDL_LoadWAV(fpat, &spec, &ptr, &len);
|
|
||||||
|
|
||||||
// convert the audio data to the format reflecting dev
|
|
||||||
tmp = audio_cvt(&spec, ptr, &len);
|
|
||||||
if (!tmp) free(ptr); // free the buffer if NULL was returned; failure
|
|
||||||
|
|
||||||
*out = (struct audiodata){tmp, len, audio_btoms(len)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loads the audio data into the buffer */
|
|
||||||
static inline void audio_load(void) {
|
|
||||||
for (size_t i = 0; i < AUDIO_ID_COUNT; i++)
|
|
||||||
audio_wav_load(audio_path[i], &audio_dat[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_play(u32 audio_id) {
|
|
||||||
if (!dev.id) return;
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < AUDIO_MAX && dev.audio[i].len > 0) i++;
|
|
||||||
if (i >= AUDIO_MAX) return;
|
|
||||||
|
|
||||||
dev.audio[i] = (struct audioplayer){
|
|
||||||
audio_dat[audio_id].buf,
|
|
||||||
audio_dat[audio_id].len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples) {
|
|
||||||
// initialise the audio device + specification
|
|
||||||
SDL_AudioSpec spec = {freq, fmt, ch, 0, samples, 0, 0, audiomixer, NULL};
|
|
||||||
unsigned id = SDL_OpenAudioDevice(NULL, 0, &spec, &spec, 0);
|
|
||||||
dev = (struct audiodevice){{0}, id, spec};
|
|
||||||
|
|
||||||
if (!id) return 1;
|
|
||||||
SDL_PauseAudioDevice(id, 0);
|
|
||||||
audio_load();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_free(void) {
|
|
||||||
SDL_CloseAudioDevice(dev.id);
|
|
||||||
dev = (struct audiodevice){0};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < AUDIO_ID_COUNT; i++)
|
|
||||||
free((void *)audio_dat[i].buf);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "../util/types.h"
|
|
||||||
|
|
||||||
#define AUDIO_MAX 4 // maximum number of sound effects that are allowed to play at once
|
|
||||||
|
|
||||||
struct audiodata {
|
|
||||||
const u8 *buf; // pointer to the audio buffer
|
|
||||||
u32 len; // length in bytes of the audio buffer
|
|
||||||
u32 ms; // length in miliseconds of the audio buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
enum audio_id {
|
|
||||||
AUDIO_ID_MUSIC,
|
|
||||||
AUDIO_ID_PLACE,
|
|
||||||
|
|
||||||
// leave at end, will contain count
|
|
||||||
AUDIO_ID_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct audiodata audio_dat[AUDIO_ID_COUNT];
|
|
||||||
|
|
||||||
/* loads the audio to be played, unless `AUDIO_MAX` has been reached, then this call will fail */
|
|
||||||
void audio_play(u32);
|
|
||||||
|
|
||||||
/* initialises the audio device, then loads the audio data */
|
|
||||||
int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples);
|
|
||||||
|
|
||||||
/* frees up resources held by the audio device and audio buffers */
|
|
||||||
void audio_free(void);
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "SDL_render.h"
|
|
||||||
|
|
||||||
// stores colour in a rgba format, each channel being a 8 bits wide.
|
|
||||||
typedef union {
|
|
||||||
uint32_t packed;
|
|
||||||
struct {
|
|
||||||
uint8_t a;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t r;
|
|
||||||
};
|
|
||||||
} colour32;
|
|
||||||
|
|
||||||
#define COLOUR32_BLACK ((colour32){0x000000FF})
|
|
||||||
#define COLOUR32_RED ((colour32){0xFF0000FF})
|
|
||||||
#define COLOUR32_YELLOW ((colour32){0xFFFF00FF})
|
|
||||||
#define COLOUR32_ORANGE ((colour32){0xFF6D00FF})
|
|
||||||
#define COLOUR32_GREEN ((colour32){0x00FF00FF})
|
|
||||||
#define COLOUR32_CYAN ((colour32){0x00FFFFFF})
|
|
||||||
#define COLOUR32_BLUE ((colour32){0x0000FFFF})
|
|
||||||
#define COLOUR32_MAGENTA ((colour32){0xFF00FFFF})
|
|
||||||
#define COLOUR32_WHITE ((colour32){0xFFFFFFFF})
|
|
||||||
|
|
||||||
// sets the render colour to a colour32 value
|
|
||||||
static inline void set_colour32(SDL_Renderer *const renderer, const colour32 c) {
|
|
||||||
(void)SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// american macros:
|
|
||||||
#define color32 colour32
|
|
||||||
#define COLOR32_BLACK COLOUR32_BLACK
|
|
||||||
#define COLOR32_RED COLOUR32_RED
|
|
||||||
#define COLOR32_YELLOW COLOUR32_YELLOW
|
|
||||||
#define COLOR32_ORANGE COLOUR32_ORANGE
|
|
||||||
#define COLOR32_GREEN COLOUR32_GREEN
|
|
||||||
#define COLOR32_CYAN COLOUR32_CYAN
|
|
||||||
#define COLOR32_BLUE COLOUR32_BLUE
|
|
||||||
#define COLOR32_MAGENTA COLOUR32_MAGENTA
|
|
||||||
#define COLOR32_WHITE COLOUR32_WHITE
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "SDL_render.h"
|
|
||||||
|
|
||||||
// stores colour in a rrrgggbb format, which maps exactly to 8 bits
|
|
||||||
typedef uint8_t colour8;
|
|
||||||
|
|
||||||
/* rrrg ggbb */
|
|
||||||
#define COLOUR8_BLACK ((colour8)0x00) // 0000 0000
|
|
||||||
#define COLOUR8_RED ((colour8)0xE0) // 1110 0000
|
|
||||||
#define COLOUR8_YELLOW ((colour8)0xFC) // 1111 1100
|
|
||||||
#define COLOUR8_ORANGE ((colour8)0xEC) // 1111 1100
|
|
||||||
#define COLOUR8_GREEN ((colour8)0x1C) // 0001 1100
|
|
||||||
#define COLOUR8_CYAN ((colour8)0x1F) // 0001 1111
|
|
||||||
#define COLOUR8_BLUE ((colour8)0x03) // 0000 0011
|
|
||||||
#define COLOUR8_MAGENTA ((colour8)0xE3) // 1110 0011
|
|
||||||
#define COLOUR8_WHITE ((colour8)0xFF) // 1111 1111
|
|
||||||
|
|
||||||
// gets the red channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_red32(const colour8 colour) {
|
|
||||||
return (colour >> 5) * (255 / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the green channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_green32(const colour8 colour) {
|
|
||||||
return ((colour >> 2) & 7) * (255 / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the blue channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_blue32(const colour8 colour) {
|
|
||||||
return (colour & 3) * (255 / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sets the render colour to a colour8 value
|
|
||||||
static inline void set_colour8(SDL_Renderer *const renderer, const colour8 c) {
|
|
||||||
(void)SDL_SetRenderDrawColor(renderer, colour8_red32(c), colour8_green32(c), colour8_blue32(c), 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
// american macros:
|
|
||||||
#define color8 colour8
|
|
||||||
#define color8_red32 colour8_red32
|
|
||||||
#define color8_green32 colour8_green32
|
|
||||||
#define color8_blue32 colour8_blue32
|
|
||||||
#define COLOR8_BLACK COLOUR8_BLACK
|
|
||||||
#define COLOR8_RED COLOUR8_RED
|
|
||||||
#define COLOR8_YELLOW COLOUR8_YELLOW
|
|
||||||
#define COLOR8_ORANGE COLOUR8_ORANGE
|
|
||||||
#define COLOR8_GREEN COLOUR8_GREEN
|
|
||||||
#define COLOR8_CYAN COLOUR8_CYAN
|
|
||||||
#define COLOR8_BLUE COLOUR8_BLUE
|
|
||||||
#define COLOR8_MAGENTA COLOUR8_MAGENTA
|
|
||||||
#define COLOR8_WHITE COLOUR8_WHITE
|
|
||||||
@@ -1,82 +1,8 @@
|
|||||||
#include "input.h"
|
#include <GLFW/glfw3.h>
|
||||||
|
void key_callback(GLFWwindow *win, int key, int scancode, int action, int mods) {
|
||||||
#include <SDL_events.h>
|
(void)win, (void)key, (void)scancode, (void)action, (void)mods;
|
||||||
#include <SDL_scancode.h>
|
#ifndef NDEBUG
|
||||||
#include <time.h>
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||||
|
glfwSetWindowShouldClose(win, 1);
|
||||||
#include "../game/time.h"
|
#endif
|
||||||
#include "../util/types.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
/* processes an incoming scancode, returns the associated movement data, or performs the close action directly
|
|
||||||
* NOTE: if the action is mapped to multiple keys, pressing both and then releasing one, disables the action. Minor issue, won't fix. */
|
|
||||||
__attribute__((const)) static int procscancode(SDL_Scancode code) {
|
|
||||||
switch (code) {
|
|
||||||
case SDL_SCANCODE_Q:
|
|
||||||
return MOVRL;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_E:
|
|
||||||
return MOVRR;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LEFT:
|
|
||||||
case SDL_SCANCODE_A:
|
|
||||||
case SDL_SCANCODE_H:
|
|
||||||
return MOVL;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RIGHT:
|
|
||||||
case SDL_SCANCODE_D:
|
|
||||||
case SDL_SCANCODE_L:
|
|
||||||
return MOVR;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_DOWN:
|
|
||||||
case SDL_SCANCODE_S:
|
|
||||||
case SDL_SCANCODE_J:
|
|
||||||
case SDL_SCANCODE_SPACE:
|
|
||||||
return MOVD;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_ESCAPE:
|
|
||||||
window_close();
|
|
||||||
return 0;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int timeout_mask(time_t time) {
|
|
||||||
static time_t timeout = 0, timeout_roll = 0;
|
|
||||||
int msk = 0;
|
|
||||||
// only add to the mask if time_poll returns `1`, negating becomes `-1`; 0b1111...
|
|
||||||
// this is masked with the desired movement action.
|
|
||||||
msk |= ((MOVR | MOVL | MOVD) & -!!time_poll(time, 64, &timeout));
|
|
||||||
msk |= ((MOVRL | MOVRR) & -!!time_poll(time, 100, &timeout_roll));
|
|
||||||
return msk;
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_getdat(time_t time) {
|
|
||||||
static u8 movdat = 0, nmovdat = 0, lmovdat = 0; // stores the static movement data
|
|
||||||
int mov = movdat, nmov = nmovdat, lmov = lmovdat; // stores the runtime movement data for easy register access
|
|
||||||
|
|
||||||
// process the events
|
|
||||||
SDL_Event e;
|
|
||||||
while (SDL_PollEvent(&e)) {
|
|
||||||
switch (e.type) {
|
|
||||||
case SDL_QUIT: window_close(); break;
|
|
||||||
case SDL_KEYDOWN: mov |= procscancode(e.key.keysym.scancode); break;
|
|
||||||
case SDL_KEYUP: nmov |= procscancode(e.key.keysym.scancode); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute the current movement
|
|
||||||
int mask = timeout_mask(time);
|
|
||||||
|
|
||||||
// handle releasing of keys
|
|
||||||
mov &= ~(nmov & lmov & mask); // only remove the keys that have been pressed since lmov
|
|
||||||
lmov = (mov & mask) | (lmov & ~mask); // set the value of lmov to the new value mov
|
|
||||||
nmov &= mov; // set nmov to only those in mov
|
|
||||||
int cmov = mov & mask;
|
|
||||||
|
|
||||||
// write to static variables (shrinking the values, and memory usage)
|
|
||||||
movdat = mov;
|
|
||||||
lmovdat = lmov;
|
|
||||||
nmovdat = nmov;
|
|
||||||
return cmov;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
#pragma once
|
#ifndef INPUT_H
|
||||||
|
#define INPUT_H 1
|
||||||
#include <time.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
void key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
|
||||||
/* 8 bit enumeration storing the movement data */
|
#endif
|
||||||
enum movdat {
|
|
||||||
MOVL = 1, // move left
|
|
||||||
MOVR = 2, // move right
|
|
||||||
MOVD = 4, // move down
|
|
||||||
MOVRL = 8, // move roll left
|
|
||||||
MOVRR = 16, // move roll right
|
|
||||||
MOVPL = 32, // move place
|
|
||||||
};
|
|
||||||
|
|
||||||
/* returns an OR'd string from `enum movdat`, containing the movement data.
|
|
||||||
* assumes that SDL has been initialized and a window has successfully been created. */
|
|
||||||
__attribute__((__pure__)) int input_getdat(time_t time);
|
|
||||||
|
|||||||
133
src/io/render.c
133
src/io/render.c
@@ -1,125 +1,30 @@
|
|||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
#include <SDL_error.h>
|
#include <glad/gl.h>
|
||||||
#include <SDL_pixels.h>
|
|
||||||
#include <SDL_rect.h>
|
|
||||||
#include <SDL_render.h>
|
|
||||||
#include <SDL_surface.h>
|
|
||||||
#include <SDL_ttf.h>
|
|
||||||
#include <SDL_video.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "../error.h"
|
#include <GLFW/glfw3.h>
|
||||||
#include "../game/game.h"
|
|
||||||
#include "../game/tetromino/shapes.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "colour/colour32.h"
|
|
||||||
#include "colour/colour8.h"
|
|
||||||
|
|
||||||
#define COLOUR_SCORE COLOUR32_YELLOW
|
#include "shader.h"
|
||||||
|
#include "../util/error.h"
|
||||||
|
|
||||||
SDL_Renderer *rend = NULL;
|
GLuint pipe;
|
||||||
TTF_Font *font = NULL;
|
|
||||||
struct gamedata const *gdat = NULL;
|
|
||||||
|
|
||||||
static SDL_Surface *score_surface = NULL;
|
int render_init(void) {
|
||||||
static SDL_Texture *score_texture = NULL;
|
pipe = glCreateProgram();
|
||||||
|
shader_init(pipe);
|
||||||
|
glLinkProgram(pipe);
|
||||||
|
glValidateProgram(pipe);
|
||||||
|
|
||||||
static inline i32 colpos(uint column) {
|
int len;
|
||||||
return column * BLOCK_WIDTH + 1 + TET_PADDING;
|
glGetProgramiv(pipe, GL_INFO_LOG_LENGTH, &len);
|
||||||
}
|
if (len > 0) {
|
||||||
|
char log[len];
|
||||||
static inline i32 rowpos(uint row) {
|
glGetProgramInfoLog(pipe, len, &len, log);
|
||||||
return row * BLOCK_HEIGHT + 1 + TET_PADDING;
|
log[len - 1] = '\0'; // terminate the string one character sooner since the log includes a newline
|
||||||
}
|
fatal("error whilst linking the pipe: '%s'", log);
|
||||||
|
|
||||||
static void draw_score_text(void) {
|
|
||||||
static u16 prev_pts = 0xFFFF; // initialise to maximum, so the code below is triggered on first run
|
|
||||||
|
|
||||||
if (prev_pts ^ gdat->pnts) {
|
|
||||||
char score_text[6] = "0"; // max (base 10) digits of a u16 + null terminator
|
|
||||||
prev_pts = gdat->pnts;
|
|
||||||
if (gdat->pnts) sprintf(score_text, "%hu0", gdat->pnts);
|
|
||||||
|
|
||||||
SDL_FreeSurface(score_surface);
|
|
||||||
SDL_DestroyTexture(score_texture);
|
|
||||||
score_surface = TTF_RenderText_Solid(font, score_text, (SDL_Colour){COLOUR_SCORE.r, COLOUR_SCORE.g, COLOUR_SCORE.b, COLOUR_SCORE.a});
|
|
||||||
score_texture = SDL_CreateTextureFromSurface(rend, score_surface);
|
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
assert(score_surface && score_texture);
|
|
||||||
SDL_Rect text_rect = {colpos(COLUMNS + 1), rowpos(0), score_surface->w, score_surface->h};
|
|
||||||
SDL_RenderCopy(rend, score_texture, NULL, &text_rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int draw_block(SDL_Renderer *const renderer, i8vec2 pos) {
|
void render_update(GLFWwindow *win) {
|
||||||
const SDL_Rect block = {colpos(pos[VX]), rowpos(pos[VY]), BLOCK_WIDTH - 1, BLOCK_HEIGHT - 1};
|
|
||||||
return SDL_RenderFillRect(renderer, &block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_shape(const u8 id, i8vec2 pos) {
|
|
||||||
set_colour8(rend, colour_from_id(id));
|
|
||||||
i8vec2 bpos[4];
|
|
||||||
shape_getblocks(id, bpos);
|
|
||||||
draw_block(rend, pos + bpos[0]);
|
|
||||||
draw_block(rend, pos + bpos[1]);
|
|
||||||
draw_block(rend, pos + bpos[2]);
|
|
||||||
draw_block(rend, pos + bpos[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_level(void) {
|
|
||||||
for (int y = 0; y < ROWS; y++) {
|
|
||||||
const u8 *row = gdat->rows[y];
|
|
||||||
|
|
||||||
for (int x = 0; x < COLUMNS; x++) {
|
|
||||||
if (row[x] != 0) {
|
|
||||||
set_colour8(rend, row[x]);
|
|
||||||
draw_block(rend, (i8vec2){x, y});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_init(SDL_Window *win, struct gamedata const *game_data) {
|
|
||||||
rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
|
||||||
if (rend == NULL) fatal(ERROR_SDL_RENDERING_INIT, "Renderer failed to be created! SDL Error: %s", SDL_GetError());
|
|
||||||
|
|
||||||
font = TTF_OpenFont("pixeldroid_botic-regular.ttf", PX_DENS);
|
|
||||||
if (font == NULL) error("Failed to open font! TTF Error: %s", TTF_GetError());
|
|
||||||
|
|
||||||
gdat = game_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_update(void) {
|
|
||||||
set_colour32(rend, COLOUR32_BLACK);
|
|
||||||
SDL_RenderClear(rend);
|
|
||||||
set_colour32(rend, COLOUR32_WHITE);
|
|
||||||
|
|
||||||
static const SDL_Rect field_size = {TET_PADDING, TET_PADDING, TET_WIDTH + 1, TET_HEIGHT + 1};
|
|
||||||
SDL_RenderDrawRect(rend, &field_size);
|
|
||||||
|
|
||||||
if (font) draw_score_text();
|
|
||||||
|
|
||||||
render_level();
|
|
||||||
draw_shape(gdat->pdat.cur, gdat->pdat.sel);
|
|
||||||
draw_shape(gdat->pdat.nxt[gdat->pdat.idx], (i8vec2){COLUMNS + 1, 3});
|
|
||||||
|
|
||||||
SDL_RenderPresent(rend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_free(void) {
|
|
||||||
assert(rend);
|
|
||||||
SDL_DestroyRenderer(rend);
|
|
||||||
rend = NULL;
|
|
||||||
|
|
||||||
TTF_CloseFont(font);
|
|
||||||
SDL_FreeSurface(score_surface);
|
|
||||||
SDL_DestroyTexture(score_texture);
|
|
||||||
font = NULL;
|
|
||||||
score_surface = NULL;
|
|
||||||
score_texture = NULL;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
#pragma once
|
#ifndef RENDER_H
|
||||||
|
#define RENDER_H 1
|
||||||
|
#include <glad/gl.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include <SDL_video.h>
|
int render_init(void);
|
||||||
#include <stdint.h>
|
void render_update(GLFWwindow *win);
|
||||||
#include <sys/cdefs.h>
|
#endif
|
||||||
|
|
||||||
#include "../game/game.h"
|
|
||||||
|
|
||||||
#define PX_DENS 25 // pixel density; pixels per block
|
|
||||||
#define TET_PADDING 10 // padding around the tetris playing field
|
|
||||||
#define TET_WIDTH (COLUMNS * PX_DENS - TET_PADDING) // tetris playing field width
|
|
||||||
#define TET_HEIGHT (TET_WIDTH / COLUMNS * ROWS) // tetris playing field height
|
|
||||||
#define SCREEN_WIDTH ((COLUMNS + 6) * PX_DENS) // window width
|
|
||||||
#define SCREEN_HEIGHT ((COLUMNS) * PX_DENS / COLUMNS * ROWS) // window height
|
|
||||||
#define BLOCK_WIDTH (TET_WIDTH / COLUMNS) // width of a block
|
|
||||||
#define BLOCK_HEIGHT (TET_HEIGHT / ROWS) // height of a block
|
|
||||||
|
|
||||||
__nonnull((1, 2)) void render_init(SDL_Window *, struct gamedata const *);
|
|
||||||
void render_update(void); // causes a draw to occur, will also determine update rate
|
|
||||||
void render_free(void); // frees the memory allocated to the renderer in render_data
|
|
||||||
|
|||||||
50
src/io/shader.c
Normal file
50
src/io/shader.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "shader.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../util/error.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* for compiling a shader */
|
||||||
|
static GLuint shader_compile(GLenum type, const char *src, size_t len) {
|
||||||
|
int ilen = len;
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
glShaderSource(shader, 1, &src, &ilen);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
/* Repurposing ilen for the max length of the shader log */
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &ilen);
|
||||||
|
if (ilen > 0) {
|
||||||
|
char log[ilen];
|
||||||
|
glGetShaderInfoLog(shader, ilen, &ilen, log);
|
||||||
|
log[ilen - 1] = '\0'; // terminate the string one character sooner since the log includes a newline
|
||||||
|
error("error whilst compiling shader type '0X%X': '%s'", type, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NAME_START(name) _binary_res_##name##_start /* name of a start variable */
|
||||||
|
#define NAME_END(name) _binary_res_##name##_end /* name of an end variable */
|
||||||
|
|
||||||
|
/* wrapper for calling `shader_compile`, but with some more sane parameters. */
|
||||||
|
#define ADD_SHADER(_type, _name, _store) \
|
||||||
|
do { \
|
||||||
|
extern const char NAME_START(_name)[]; \
|
||||||
|
extern const char NAME_END(_name)[]; \
|
||||||
|
(_store) = shader_compile(_type, NAME_START(_name), (size_t)NAME_END(_name) - (size_t)NAME_START(_name)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int shader_init(GLuint pipe) {
|
||||||
|
GLuint vs, fs;
|
||||||
|
ADD_SHADER(GL_VERTEX_SHADER, sh_vert_glsl, vs);
|
||||||
|
ADD_SHADER(GL_FRAGMENT_SHADER, sh_frag_glsl, fs);
|
||||||
|
|
||||||
|
glAttachShader(pipe, vs);
|
||||||
|
glAttachShader(pipe, fs);
|
||||||
|
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
5
src/io/shader.h
Normal file
5
src/io/shader.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#ifndef SHADER_H
|
||||||
|
#define SHADER_H 1
|
||||||
|
#include <glad/gl.h>
|
||||||
|
int shader_init(GLuint pipe);
|
||||||
|
#endif
|
||||||
@@ -1,60 +1,64 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <SDL_error.h>
|
|
||||||
#include <SDL_video.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <time.h>
|
#include <glad/gl.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include "../error.h"
|
#include "../tetris.h"
|
||||||
#include "../game/game.h"
|
#include "../util/error.h"
|
||||||
#include "../game/time.h"
|
|
||||||
#include "SDL_ttf.h"
|
|
||||||
#include "audio.h"
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
static SDL_Window *win = NULL;
|
#define WIN_DPI 32
|
||||||
static bool close = false;
|
#define WIN_WIDTH (WIN_DPI * (TET_WIDTH + 3))
|
||||||
|
#define WIN_HEIGHT (WIN_DPI * TET_HEIGHT)
|
||||||
|
|
||||||
void window_init(struct gamedata const *gdat) {
|
static GLFWwindow *win = NULL;
|
||||||
assert(!win && !close);
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
|
||||||
fatal(ERROR_SDL_INIT, "SDL could not initialize! SDL Error: %s", SDL_GetError());
|
|
||||||
if (TTF_Init() < 0)
|
|
||||||
fatal(ERROR_SDL_INIT, "TTF could not initialize! TTF Error: %s", TTF_GetError());
|
|
||||||
|
|
||||||
win = SDL_CreateWindow("tetris clone", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
|
int window_init(void) {
|
||||||
if (win == NULL)
|
#ifndef NDEBUG
|
||||||
fatal(ERROR_SDL_RENDERING_INIT, "Window failed to be created! SDL Error: %s", SDL_GetError());
|
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
||||||
|
#endif
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||||
|
glfwWindowHint(GLFW_RED_BITS, 3);
|
||||||
|
glfwWindowHint(GLFW_GREEN_BITS, 3);
|
||||||
|
glfwWindowHint(GLFW_BLUE_BITS, 2);
|
||||||
|
glfwWindowHint(GLFW_ALPHA_BITS, 0);
|
||||||
|
win = glfwCreateWindow(WIN_WIDTH, WIN_HEIGHT, "Quinn's tetris", NULL, NULL);
|
||||||
|
if (!win) return 1;
|
||||||
|
|
||||||
render_init(win, gdat);
|
glfwMakeContextCurrent(win);
|
||||||
audio_init(32000, AUDIO_S16, 1, 4096);
|
if (!gladLoadGL(glfwGetProcAddress)) return 1;
|
||||||
|
glfwSwapInterval(1); /* Wait one screen update for redraw. A.k.a. "vsync" */
|
||||||
|
|
||||||
|
glfwSetKeyCallback(win, key_callback);
|
||||||
|
|
||||||
|
debug(
|
||||||
|
"version info:\n"
|
||||||
|
"\tvendor: %s\n"
|
||||||
|
"\trenderer: %s\n"
|
||||||
|
"\tversion: %s\n"
|
||||||
|
"\tshading lang: %s\n",
|
||||||
|
glGetString(GL_VENDOR),
|
||||||
|
glGetString(GL_RENDERER),
|
||||||
|
glGetString(GL_VERSION),
|
||||||
|
glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||||
|
|
||||||
|
return 0; //render_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void window_free(void) {
|
void window_loop(void) {
|
||||||
assert(win);
|
assert(win != NULL);
|
||||||
render_free();
|
while (!glfwWindowShouldClose(win)) {
|
||||||
SDL_DestroyWindow(win);
|
glfwWaitEventsTimeout(1.0 / 60.0);
|
||||||
win = NULL;
|
|
||||||
close = false;
|
|
||||||
|
|
||||||
audio_free();
|
// render_update(win);
|
||||||
}
|
glfwSwapBuffers(win);
|
||||||
|
|
||||||
void window_open(void) {
|
|
||||||
while (!close) {
|
|
||||||
size_t time = time_pull();
|
|
||||||
game_update(input_getdat(time), time);
|
|
||||||
render_update();
|
|
||||||
|
|
||||||
static time_t timeout = 0;
|
|
||||||
if (time_poll(time, audio_dat[AUDIO_ID_MUSIC].ms, &timeout))
|
|
||||||
audio_play(AUDIO_ID_MUSIC);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void window_close(void) {
|
glfwDestroyWindow(win);
|
||||||
close = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
#pragma once
|
#ifndef WINDOW_H
|
||||||
|
#define WINDOW_H 1
|
||||||
#include "../game/game.h"
|
int window_init(void);
|
||||||
|
void window_loop(void);
|
||||||
#define PX_DENS 25 // pixel density; pixels per block
|
#endif
|
||||||
#define SCREEN_WIDTH ((COLUMNS + 6) * PX_DENS) // window width
|
|
||||||
#define SCREEN_HEIGHT ((COLUMNS) * PX_DENS / COLUMNS * ROWS) // window height
|
|
||||||
|
|
||||||
void window_init(struct gamedata const *);
|
|
||||||
void window_open(void);
|
|
||||||
void window_close(void);
|
|
||||||
void window_free(void);
|
|
||||||
|
|||||||
42
src/main.c
42
src/main.c
@@ -1,25 +1,33 @@
|
|||||||
#include <SDL.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "error.h"
|
|
||||||
#include "game/game.h"
|
|
||||||
#include "io/window.h"
|
#include "io/window.h"
|
||||||
|
#include "util/error.h"
|
||||||
|
|
||||||
static void stop(void) {
|
/* callback for GLFW errors */
|
||||||
debug("stopping...", );
|
static void error_callback(int err, const char *const msg) {
|
||||||
window_close();
|
error("glfw (%i); \"%s\"", err, msg);
|
||||||
window_free();
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// entry-point of the application
|
static int init(void) {
|
||||||
int main(int argc, char **argv) {
|
glfwSetErrorCallback(error_callback);
|
||||||
(void)argc, (void)argv;
|
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); // disable joystick buttons
|
||||||
// register stop as exit function
|
|
||||||
atexit(stop);
|
|
||||||
|
|
||||||
window_init(game_init());
|
|
||||||
window_open();
|
|
||||||
|
|
||||||
|
if (!glfwInit()) return 1;
|
||||||
|
if (window_init()) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void quit(void) {
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
(void)argc, (void)argv;
|
||||||
|
printf("debug: [DBG], info: [INF], warning: [WAR], error: [ERR], fatal: [FAT]\n");
|
||||||
|
atexit(quit);
|
||||||
|
if (init()) fatal("failed to initialise the programme!");
|
||||||
|
window_loop();
|
||||||
|
quit();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined __unix__
|
|
||||||
#include <features.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#elif defined _WIN32
|
|
||||||
#include <io.h>
|
|
||||||
#else
|
|
||||||
#error platform not supported!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define PATH_SEP '\\' // contains the path separator as a character. Yes it is extremely annoying that this has to exist.
|
|
||||||
#define PATH_SEP_STR "\\" // contains the path separator as a string, useful for concatenation. Yes it is extremely annoying that this has to exist.
|
|
||||||
|
|
||||||
#define unixonly(_exec) // (no-op) executes inline code when __unix__ is defined, otherwise is no-op
|
|
||||||
#define winonly(_exec) _exec // executes inline code when _WIN32 is defined, otherwise is no-op
|
|
||||||
#else
|
|
||||||
#define PATH_SEP '/' // contains the path separator as a character. Yes it is extremely annoying that this has to exist.
|
|
||||||
#define PATH_SEP_STR "/" // contains the path separator as a string, useful for concatenation. Yes it is extremely annoying that this has to exist.
|
|
||||||
|
|
||||||
#define unixonly(_exec) _exec // executes inline code when __unix__ is defined, otherwise is no-op
|
|
||||||
#define winonly(_exec) // (no-op) executes inline code when _WIN32 is defined, otherwise is no-op
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// define the constants if they haven't been
|
|
||||||
#ifndef F_OK
|
|
||||||
#define F_OK 0
|
|
||||||
#endif
|
|
||||||
#ifndef X_OK
|
|
||||||
#define X_OK 1
|
|
||||||
#endif
|
|
||||||
#ifndef W_OK
|
|
||||||
#define W_OK 2
|
|
||||||
#endif
|
|
||||||
#ifndef R_OK
|
|
||||||
#define R_OK 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum faccess_perms {
|
|
||||||
FA_F = F_OK, // test for file's existence
|
|
||||||
FA_X = X_OK, // test for executing permission
|
|
||||||
FA_W = W_OK, // test for write permissions
|
|
||||||
FA_R = R_OK, // test for read permissions
|
|
||||||
};
|
|
||||||
|
|
||||||
/* tests a files access with F_OK, X_OK, R_OK, W_OK OR'd together
|
|
||||||
returns 0 upon success. -1 when errno is set and anything else when one or more of the permissions isn't set */
|
|
||||||
static inline int faccess(const char *restrict fname, int perms) {
|
|
||||||
#if defined __unix__ && _POSIX_C_SOURCE >= 200809L
|
|
||||||
return access(fname, perms);
|
|
||||||
#elif defined _WIN32
|
|
||||||
return _access(fname, perms);
|
|
||||||
#else
|
|
||||||
#error platform unsupported!
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user