diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..4fa3689 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.22000.0", + "compilerPath": "C:/mingw64/bin/gcc.exe", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "windows-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..646f1fa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,55 @@ +{ + "files.associations": { + "pthread.h": "c", + "fumotris.h": "c", + "string.h": "c", + "input.h": "c", + "stdint.h": "c", + "iso646.h": "c", + "win.h": "c", + "array": "cpp", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "string_view": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "execution": "cpp" + } +} \ No newline at end of file diff --git a/checksums.txt b/checksums.txt new file mode 100644 index 0000000..09411bb --- /dev/null +++ b/checksums.txt @@ -0,0 +1 @@ +{"source\\main.c": "3d2369e9333b52142cb9f019e877e434", "source\\io\\controller.c": "4459cc302f4b70b072f39b43e87a0c08", "source\\io\\input.c": "d3336a096a3ac2d792292d84dd20fb8f", "source\\io\\platforms\\win.c": "4eb14343a9cb40a7f6a9d9e43329c46e", "source\\io\\platforms\\winio.c": "b3a5e9d8f2e8ab4ec38d14007e4fc3f4"} \ No newline at end of file diff --git a/objects/controller.o b/objects/controller.o new file mode 100644 index 0000000..6eb9011 Binary files /dev/null and b/objects/controller.o differ diff --git a/objects/input.o b/objects/input.o new file mode 100644 index 0000000..bcac4d2 Binary files /dev/null and b/objects/input.o differ diff --git a/objects/main.o b/objects/main.o new file mode 100644 index 0000000..b3ddca7 Binary files /dev/null and b/objects/main.o differ diff --git a/objects/win.o b/objects/win.o new file mode 100644 index 0000000..515e292 Binary files /dev/null and b/objects/win.o differ diff --git a/run.py b/run.py new file mode 100644 index 0000000..546fe20 --- /dev/null +++ b/run.py @@ -0,0 +1,94 @@ +import os, sys +import json, hashlib + + +def walk_source(path): + source_paths = [] + subdirs = [] + + for dirpath, dirnames, filenames in os.walk(path): + source_paths += [os.path.join(dirpath, f) for f in filenames if f.endswith(".c")] + subdirs.append(dirpath) + + return (source_paths, subdirs) + + +def get_checksums(source_files): + checksums = {} + + for path in source_files: + with open(path, "rb") as source_file: + source = source_file.read() + + checksum = hashlib.md5(source).hexdigest() + checksums[path] = checksum + + return checksums + + +def read_checksum_file(): + checksums = {} + + if not os.path.exists("checksums.txt"): + return checksums + + with open("checksums.txt", "rb") as checksum_file: + checksums = json.loads(checksum_file.read()) + + return checksums + + +def write_checksum_file(checksums): + with open("checksums.txt", "w+") as checksum_file: + checksum_file.write(json.dumps(checksums)) + + +def get_object_names(path): + object_names = [] + + for file in os.listdir(path): + if os.path.isfile(os.path.join(path, file)): + name = os.path.splitext(os.path.basename(file))[0] + + object_names.append(name) + + return object_names + + +def build(source_path, obj_path, out_path, recompile = False): + source_paths, subdirs = walk_source(source_path) + + if recompile: + result = os.system(f"gcc {' '.join(source_paths)} -I {' -I '.join(subdirs)} -o {out_path} -pthread -Wall -std=c17 -pedantic") + print(result) + return + + + checksums_before = read_checksum_file() + checksums_now = get_checksums(source_paths) + + object_names = get_object_names(obj_path) + compile_list = [] + + for path in checksums_now: + name = os.path.splitext(os.path.basename(path))[0] + + if path not in checksums_before or checksums_before[path] != checksums_now[path] or name not in object_names: + compile_list.append(path) + + if name in object_names: + object_names.remove(name) + + for object_name in object_names: + os.remove(f"{obj_path}\\{object_name}.o") + + for path in compile_list: + name = os.path.splitext(os.path.basename(path))[0] + + os.system(f"gcc -c {path} -I {' -I '.join(subdirs)} -o {obj_path}\\{name}.o -pthread -Wall -std=c17 -pedantic") + + write_checksum_file(checksums_now) + print(os.system(f"gcc {obj_path}\\*.o -o {out_path} -pthread -Wall -std=c17 -pedantic")) + + +build(sys.argv[1], sys.argv[2], sys.argv[3], True) \ No newline at end of file diff --git a/source/fumotris.h b/source/fumotris.h new file mode 100644 index 0000000..ad30993 --- /dev/null +++ b/source/fumotris.h @@ -0,0 +1,17 @@ +#pragma once +#include + +#define nullptr ((void*)0) + +typedef uint8_t u8; +typedef uint_fast8_t u8f; + +typedef uint16_t u16; +typedef uint_fast16_t u16f; + +typedef uint32_t u32; +typedef uint_fast32_t u32f; + +typedef uint64_t u64; +typedef uint_fast64_t u64f; + diff --git a/source/game/gametime.c b/source/game/gametime.c new file mode 100644 index 0000000..f9bc5df --- /dev/null +++ b/source/game/gametime.c @@ -0,0 +1,21 @@ +#include +#include + +#ifdef _WIN32 +#include "win.h" +#endif + +double GetTime() +{ + struct timespec ts; + timespec_get(&ts, TIME_UTC); + + return ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; +} + +bool Wait(double seconds) +{ + #ifdef _WIN32 + return WindowsWait(seconds); + #endif +} \ No newline at end of file diff --git a/source/game/gametime.h b/source/game/gametime.h new file mode 100644 index 0000000..a660ebb --- /dev/null +++ b/source/game/gametime.h @@ -0,0 +1,6 @@ +#pragma once +#include + +double GetTime(); + +bool Wait(double seconds); \ No newline at end of file diff --git a/source/game/tetr.c b/source/game/tetr.c new file mode 100644 index 0000000..6fe475e --- /dev/null +++ b/source/game/tetr.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" +#include "term.h" + +struct TetrMap { + size_t wid; + size_t hgt; + size_t area; + + int x; + int y; + u8 rot; + + u8 *blks; +}; + +struct TetrMap NewTetrMap(size_t wid, size_t hgt) +{ + return (struct TetrMap) { + wid, hgt, wid * hgt, + 0, 0, 0, + }; +} + +void TetrMapToTermBuf(struct TetrMap *map, struct TermBuf *term) +{ + static const u8f blk_colors[8] = { 8, 14, 11, 13, 10, 9, 12, 3 }; + + for (size_t y = 0; y < map->hgt; y++) { + for (size_t x = 0; x < map->wid; x++) { + size_t map_i = y * map->wid + x; + size_t buf_i = (y + map->y) * term->wid + (x + map->x) * 2; + + struct CharBlk4 *a = &term->blks[buf_i]; + struct CharBlk4 *b = &term->blks[buf_i + 1]; + + if (map->blks[map_i] == 0) { + a->ch = '('; + b->ch = ')'; + } else { + a->ch = '['; + b->ch = ']'; + } + + u8 fg = blk_colors[map->blks[map_i]]; + a->fg = fg; + b->fg = fg; + } + } +} + +bool TetrCollisionCheck(struct TetrMap *board, struct TetrMap *piece, int dx, int dy) +{ + size_t i = 0; + for (size_t y = piece->y + dy; y < piece->y + piece->hgt + dy; y++) { + for (size_t x = piece->x + dx; x < piece->x + piece->wid + dx; x++) { + if(piece->blks[i] == 0) + goto next; + + if(y >= board->hgt or x >= board->wid) + return false; + + size_t board_i = y * board->wid + x; + if(board->blks[board_i] != 0) + return false; + +next: + i++; + } + } + return true; +} \ No newline at end of file diff --git a/source/game/tetr.h b/source/game/tetr.h new file mode 100644 index 0000000..5efe26f --- /dev/null +++ b/source/game/tetr.h @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" +#include "term.h" + +struct TetrMap { + size_t wid; + size_t hgt; + size_t area; + + int x; + int y; + u8 rot; + + u8 *blks; +}; + +struct TetrMap NewTetrMap(size_t wid, size_t hgt); + +void TetrMapToTermBuf(struct TetrMap *map, struct TermBuf *term); + +bool TetrCollisionCheck(struct TetrMap *board, struct TetrMap *piece, int dx, int dy); \ No newline at end of file diff --git a/source/io/ctrl.c b/source/io/ctrl.c new file mode 100644 index 0000000..83776c8 --- /dev/null +++ b/source/io/ctrl.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" + +#define IO_BUF_SIZE 8 + +enum InputType { + KEY, + AXIS, + JOYSTICK, + WINDOW, + ESCAPE +}; + +struct InputRecord { + enum InputType type; + u16 id; + + union { + struct { + u32 value; + bool is_down; + } axis; + struct { + u32 x; + u32 y; + } joystick; + } data; + + double timestamp; +}; + +struct InputResult { + size_t count; + struct InputRecord buf[IO_BUF_SIZE]; +}; + +struct Axis { + union { + struct { + u32 value; + bool is_down; + } axis; + struct { + u32 x; + u32 y; + } joystick; + } data; + + double last_pressed; + double last_released; +}; + +enum KeyCode { + LEFT, + RIGHT, + SOFT_DROP, + HARD_DROP, + ROTATE_CCW, + ROTATE_CW, + ROTATE_180, + SWAP, + ESC +}; + +enum AxisCode { + VSCROLL, + HSCROLL +}; + +enum JoystickCode { + MOUSE +}; + +typedef u32 hashtype; +hashtype Hash(void *item, size_t size) +{ + u8* data = (u8*)item; + + u32 h = 98317; + for (size_t i = 0; i < size; i++) { + h ^= data[i]; + h *= 0x5bd1e995; + h ^= h >> 15; + } + + return h; +} + +struct ident { + u16 id; + enum InputType type; +}; + +hashtype hash_ident(u16f id, enum InputType type) +{ + struct ident obj = { id, type }; + return Hash(&obj, sizeof(struct ident)); +} + +struct ctrl_bkt { + hashtype bind_hash; + size_t index; + hashtype code_hash; + + struct Axis axis; +}; + +struct Ctrl { + size_t capacity; + size_t filled; + struct ctrl_bkt *bkts; + + pthread_mutex_t mutex; +}; +typedef struct Ctrl Ctrl; + +Ctrl NewCtrl(struct ctrl_bkt *bkts_prealloc, size_t capacity) +{ + Ctrl ctrl; + ctrl.capacity = capacity; + ctrl.filled = 0; + memset(bkts_prealloc, 0, sizeof(struct ctrl_bkt) * capacity); + ctrl.bkts = bkts_prealloc; + ctrl.mutex = PTHREAD_MUTEX_INITIALIZER; + return ctrl; +} + +struct ctrl_bkt *get_bkt(Ctrl *ctrl, size_t i) +{ + return &ctrl->bkts[i]; +} + +struct ctrl_bkt *find_bind(Ctrl *ctrl, hashtype bind_hash, hashtype search) +{ + size_t i = bind_hash % ctrl->capacity; + + struct ctrl_bkt *next; + for (size_t offset = 0; offset < ctrl->capacity; offset++) { + i = (i + 1) % ctrl->capacity; + + next = get_bkt(ctrl, i); + if (next->bind_hash != search) + continue; + + return next; + } + return nullptr; +} + +struct ctrl_bkt *find_code(Ctrl *ctrl, size_t *i, hashtype search) +{ + struct ctrl_bkt *next; + for (size_t offset = 0; offset < ctrl->capacity; offset++) { + *i = (*i + 1) % ctrl->capacity; + + next = get_bkt(ctrl, *i); + if (next->code_hash != search) + continue; + + return next; + } + return nullptr; +} + +bool CtrlMap(Ctrl *ctrl, u16f bind, u16f code, enum InputType type) +{ + if (ctrl->filled == ctrl->capacity) + return false; + + hashtype bind_hash = hash_ident(bind, type); + struct ctrl_bkt *bind_bkt = find_bind(ctrl, bind_hash, 0); + bind_bkt->bind_hash = bind_hash; + + hashtype code_hash = hash_ident(code, type); + size_t code_i = code_hash % ctrl->capacity; + struct ctrl_bkt *code_bkt = find_code(ctrl, &code_i, 0); + code_bkt->code_hash = code_hash; + + bind_bkt->index = code_i; + ctrl->filled += 1; + + return true; +} + +struct Axis *find_bind_axis(Ctrl *ctrl, u16f bind, enum InputType type) +{ + hashtype bind_hash = hash_ident(bind, KEY); + struct ctrl_bkt *bind_bkt = find_bind(ctrl, bind_hash, bind_hash); + if (bind_bkt == nullptr) + return nullptr; + + return &get_bkt(ctrl, bind_bkt->index)->axis; +} + +struct Axis *CtrlGet(Ctrl *ctrl, u16f code, enum InputType type) +{ + hashtype code_hash = hash_ident(code, type); + size_t code_i = code_hash % ctrl->capacity; + struct ctrl_bkt *code_bkt = find_code(ctrl, &code_i, code_hash); + if (code_bkt == nullptr) + return nullptr; + + return &code_bkt->axis; +} + +bool CtrlUpdateKey(Ctrl *ctrl, struct InputRecord *record) +{ + struct Axis *axis = find_bind_axis(ctrl, record->id, KEY); + if (axis == nullptr) + return false; + + if (record->data.axis.is_down) { + axis->last_pressed = record->timestamp; + } else { + axis->last_released = record->timestamp; + } + axis->data.axis.is_down = record->data.axis.is_down; + + return true; +} + +bool CtrlUpdateAxis(Ctrl *ctrl, struct InputRecord *record) +{ + struct Axis *axis = find_bind_axis(ctrl, record->id, AXIS); + if (axis == nullptr) + return false; + + axis->data.axis.value = record->data.axis.value; + axis->last_pressed = record->timestamp; + + return true; +} + +bool CtrlUpdateJoystick(Ctrl *ctrl, struct InputRecord *record) +{ + struct Axis *axis = find_bind_axis(ctrl, record->id, JOYSTICK); + if (axis == nullptr) + return false; + + axis->data.joystick.x = record->data.joystick.x; + axis->data.joystick.y = record->data.joystick.y; + axis->last_pressed = record->timestamp; + + return true; +} + +bool CtrlUpdateWindow(Ctrl *ctrl, struct InputRecord *record) +{ + struct Axis *axis = find_bind_axis(ctrl, record->id, WINDOW); + if (axis == nullptr) + return false; + + axis->data.joystick.x = record->data.joystick.x; + axis->data.joystick.y = record->data.joystick.y; + axis->last_pressed = record->timestamp; + + return true; +} \ No newline at end of file diff --git a/source/io/ctrl.h b/source/io/ctrl.h new file mode 100644 index 0000000..f6a8425 --- /dev/null +++ b/source/io/ctrl.h @@ -0,0 +1,114 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" + +#define IO_BUF_SIZE 8 + +enum InputType { + KEY, + AXIS, + JOYSTICK, + WINDOW, + ESCAPE +}; + +struct InputRecord { + enum InputType type; + u16 id; + + union { + struct { + u32 value; + bool is_down; + } axis; + struct { + u32 x; + u32 y; + } joystick; + } data; + + double timestamp; +}; + +struct InputResult { + size_t count; + struct InputRecord buf[IO_BUF_SIZE]; +}; + +struct Axis { + union { + struct { + u32 value; + bool is_down; + } axis; + struct { + u32 x; + u32 y; + } joystick; + } data; + + double last_pressed; + double last_released; +}; + +enum KeyCode { + LEFT, + RIGHT, + SOFT_DROP, + HARD_DROP, + ROTATE_CCW, + ROTATE_CW, + ROTATE_180, + SWAP, + ESC +}; + +enum AxisCode { + VSCROLL, + HSCROLL +}; + +enum JoystickCode { + MOUSE +}; + +typedef u32 hashtype; + +struct ctrl_bkt { + hashtype bind_hash; + size_t index; + hashtype code_hash; + + struct Axis axis; +}; + +struct Ctrl { + size_t capacity; + size_t filled; + struct ctrl_bkt *bkts; + + pthread_mutex_t mutex; +}; +typedef struct Ctrl Ctrl; + +Ctrl NewCtrl(struct ctrl_bkt *bkts_prealloc, size_t capacity); + +bool CtrlMap(Ctrl *ctrl, u16f bind, u16f code, enum InputType type); + +struct Axis *CtrlGet(Ctrl *ctrl, u16f code, enum InputType type); + +bool CtrlUpdateKey(Ctrl *ctrl, struct InputRecord *record); + +bool CtrlUpdateAxis(Ctrl *ctrl, struct InputRecord *record); + +bool CtrlUpdateJoystick(Ctrl *ctrl, struct InputRecord *record); + +bool CtrlUpdateWindow(Ctrl *ctrl, struct InputRecord *record); + diff --git a/source/io/input.c b/source/io/input.c new file mode 100644 index 0000000..1d56aad --- /dev/null +++ b/source/io/input.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include + +#include "ctrl.h" +#include "fumotris.h" +#include "gametime.h" + +#ifdef _WIN32 +#include "win.h" +#endif + +bool dispatch(Ctrl *ctrl, struct InputRecord *record) +{ + switch (record->type) { + case KEY: + return CtrlUpdateKey(ctrl, record); + case AXIS: + return CtrlUpdateAxis(ctrl, record); + case JOYSTICK: + return CtrlUpdateJoystick(ctrl, record); + case WINDOW: + return CtrlUpdateWindow(ctrl, record); + case ESCAPE: + default: + return false; + } +} + +bool write_result(Ctrl *ctrl, struct InputResult *result) +{ + double now = GetTime(); + pthread_mutex_lock(&ctrl->mutex); + + for (size_t i = 0; i < result->count; i++) { + if (result->buf[i].type == ESCAPE) + return false; + result->buf[i].timestamp = now; + + dispatch(ctrl, &result->buf[i]); + } + + pthread_mutex_unlock(&ctrl->mutex); + return true; +} + +void *block_input(void *args) +{ + Ctrl *ctrl = args; + struct InputResult result; + +input_loop: + bool success; + #ifdef _WIN32 + success = WindowsBlockInput(&result); + #endif + + if (!success) { + exit(1); + } + + if (!write_result(ctrl, &result)) { + return nullptr; + } + + goto input_loop; + + return nullptr; +} + +void StartInput(Ctrl *ctrl) +{ + pthread_t input_thread; + pthread_create(&input_thread, nullptr, block_input, ctrl); + //pthread_join(input_thread, nullptr); +} \ No newline at end of file diff --git a/source/io/input.h b/source/io/input.h new file mode 100644 index 0000000..1688a2c --- /dev/null +++ b/source/io/input.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "ctrl.h" +#include "fumotris.h" + +void StartInput(Ctrl *ctrl); \ No newline at end of file diff --git a/source/io/platforms/win.c b/source/io/platforms/win.c new file mode 100644 index 0000000..efb36cd --- /dev/null +++ b/source/io/platforms/win.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +#include "input.h" +#include "winhandler.h" + +bool WindowsInit() +{ + if (!WinInitInputHandle()) + return false; + + if (!WinInitTimer()) + return false; + + if(!WinInitConsole()) + return false; + + return true; +} + +bool WindowsBlockInput(struct InputResult *result) +{ + return WinBlockInput(result); +} + +bool WindowsWait(double seconds) +{ + return WinWait(seconds); +} \ No newline at end of file diff --git a/source/io/platforms/win.h b/source/io/platforms/win.h new file mode 100644 index 0000000..7a89e9a --- /dev/null +++ b/source/io/platforms/win.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "input.h" + +bool WindowsInit(); + +bool WindowsBlockInput(struct InputResult *result); + +bool WindowsWait(double seconds); \ No newline at end of file diff --git a/source/io/platforms/winhandler.c b/source/io/platforms/winhandler.c new file mode 100644 index 0000000..f805b1c --- /dev/null +++ b/source/io/platforms/winhandler.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" +#include "input.h" + +struct Windows { + HANDLE input_handle; + HANDLE timer; +}; +static struct Windows windows; + +bool WinInitInputHandle() +{ + windows.input_handle = GetStdHandle(STD_INPUT_HANDLE); + if (windows.input_handle == INVALID_HANDLE_VALUE) + return false; + return true; +} + +bool WinInitTimer() +{ + windows.timer = CreateWaitableTimer(NULL, TRUE, NULL); + if (!windows.timer) + return false; + return true; +} + +bool WinInitConsole() +{ + DWORD mode = ENABLE_EXTENDED_FLAGS + | ENABLE_PROCESSED_INPUT + | ENABLE_MOUSE_INPUT + | ENABLE_WINDOW_INPUT + | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + return SetConsoleMode(windows.input_handle, mode); +} + +void set_key_record(struct InputRecord *record, KEY_EVENT_RECORD win_key) +{ + record->type = KEY; + record->id = win_key.wVirtualKeyCode; + record->data.axis.is_down = win_key.bKeyDown; + + if (win_key.wVirtualKeyCode == VK_ESCAPE) + record->type = ESCAPE; +} + +bool set_mouse_record(struct InputRecord *record, MOUSE_EVENT_RECORD win_mouse) +{ + switch (win_mouse.dwEventFlags) { + case MOUSE_WHEELED: + record->type = AXIS; + record->id = 0; + record->data.axis.value = win_mouse.dwButtonState; + break; + case MOUSE_HWHEELED: + record->type = AXIS; + record->id = 1; + record->data.axis.value = win_mouse.dwButtonState; + break; + case MOUSE_MOVED: + record->type = JOYSTICK; + record->id = 0; + record->data.joystick.x = win_mouse.dwMousePosition.X; + record->data.joystick.y = win_mouse.dwMousePosition.Y; + break; + default: + return false; + } + return true; +} + +void set_window_record(struct InputRecord *record, WINDOW_BUFFER_SIZE_RECORD win_resize) +{ + record->type = WINDOW; + record->data.joystick.x = win_resize.dwSize.X; + record->data.joystick.y = win_resize.dwSize.Y; +} + +bool dispatch_record(struct InputRecord *record, INPUT_RECORD win_record) +{ + switch (win_record.EventType) { + case KEY_EVENT: + set_key_record(record, win_record.Event.KeyEvent); + return true; + case MOUSE_EVENT: + return set_mouse_record(record, win_record.Event.MouseEvent); + case WINDOW_BUFFER_SIZE_EVENT: + set_window_record(record, win_record.Event.WindowBufferSizeEvent); + return true; + default: + record->type = ESCAPE; + } + return true; +} + +bool WinBlockInput(struct InputResult *result) +{ + INPUT_RECORD buf[8]; + DWORD count; + + if (!ReadConsoleInput(windows.input_handle, buf, 8, &count)) + return false; + + size_t unused_offset = 0; + for (size_t i = 0; i < count; i++) { + struct InputRecord record; + + bool include = dispatch_record(&record, buf[i]); + if (record.type == ESCAPE) + return false; + if (!include) + unused_offset += 1; + + result->buf[i - unused_offset] = record; + } + result->count = count - unused_offset; + return true; +} + +bool WinWait(double seconds) +{ + LARGE_INTEGER duration; + duration.QuadPart = (u64)(-10000000.0 * seconds); + + if (!SetWaitableTimer(windows.timer, &duration, 0, NULL, NULL, FALSE)) + return false; + + DWORD result = WaitForSingleObject(windows.timer, INFINITE); + if (result != WAIT_OBJECT_0) + return false; + + return true; +} \ No newline at end of file diff --git a/source/io/platforms/winhandler.h b/source/io/platforms/winhandler.h new file mode 100644 index 0000000..25797d0 --- /dev/null +++ b/source/io/platforms/winhandler.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "fumotris.h" + +bool WinInitInputHandle(); + +bool WinInitTimer(); + +bool WinInitConsole(); + +bool WinBlockInput(struct InputResult *result); + +bool WinWait(double seconds); \ No newline at end of file diff --git a/source/io/term.c b/source/io/term.c new file mode 100644 index 0000000..2871b56 --- /dev/null +++ b/source/io/term.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" + +struct CharBlk4 { + char ch; + u8 bg : 4; + u8 fg : 4; +}; + +struct TermBuf { + size_t wid; + size_t hgt; + size_t area; + struct CharBlk4 *blks; +}; + +struct TermBuf NewTermBuf(size_t wid, size_t hgt) +{ + return (struct TermBuf) { + wid, hgt, wid * hgt + }; +} + +size_t TermMaxChars(struct TermBuf *term) +{ + static const size_t max_color_str_len = 10; + static const size_t reset_str_len = 7; + + return reset_str_len + + (max_color_str_len + 1) * term->area + + (term->hgt - 1) + + 1; +} + +size_t printcol4(char *buf, size_t at, size_t max, u8f col, char ch) +{ + if (col < 8) + col += 30; + else + col += 82; + + return snprintf(buf + at, max - at, "\x1b[%um%c", col, ch); +} + +size_t printblk4(char *buf, size_t at, size_t max, struct CharBlk4 *blk) +{ + u8f bg; + if (blk->bg < 8) + bg = blk->bg + 40; + else + bg = blk->bg + 92; + + u8f fg; + if (blk->fg < 8) + fg = blk->fg + 30; + else + fg = blk->fg + 82; + + return snprintf(buf + at, max - at, "\x1b[%u;%um%c", bg, fg, blk->ch); +} + +size_t TermBufToChars(struct TermBuf *term, char *buf, size_t max_chars) +{ + u8f last_bg = 0; + u8f last_fg = 0; + + size_t filled = snprintf(buf, max_chars, "\x1b[H\x1b[0m"); + + for(size_t y = 0; y < term->hgt; y++) { + for(size_t x = 0; x < term->wid; x++) { + size_t i = y * term->wid + x; + struct CharBlk4 *blk = &term->blks[i]; + + // DEBUG + if (blk->ch == 0) + blk->ch = '#'; + // DEBUG + + if (blk->bg != 0 and blk->bg != last_bg) { + last_bg = blk->bg; + if (blk->fg != 0 and blk->fg != last_fg) { + filled += printblk4(buf, filled, max_chars, blk); + last_fg = blk->fg; + } else { + filled += printcol4(buf, filled, max_chars, blk->bg, blk->ch); + } + } else if (blk->fg != 0 and blk->fg != last_fg) { + filled += printcol4(buf, filled, max_chars, blk->fg, blk->ch); + last_fg = blk->fg; + } else { + buf[filled] = blk->ch; + filled += 1; + } + } + buf[filled] = '\n'; + filled += 1; + } + buf[filled] = 0; + return filled; +} + +/*int main() +{ + struct TermBuf term; + term.wid = 20; + term.hgt = 10; + term.area = 20 * 10; + struct CharBlk4 blks[term.area]; + memset(&blks, 0, sizeof(struct CharBlk4) * term.area); + term.blks = blks; + + size_t out_max = TermMaxChars(&term); + char out[out_max]; + memset(out, 0, out_max); + + TermBufToChars(&term, out, out_max); + + puts(out); + + return 0; +}*/ \ No newline at end of file diff --git a/source/io/term.h b/source/io/term.h new file mode 100644 index 0000000..8892b03 --- /dev/null +++ b/source/io/term.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "fumotris.h" + +struct CharBlk4 { + char ch; + u8 bg : 4; + u8 fg : 4; +}; + +struct TermBuf { + size_t wid; + size_t hgt; + size_t area; + struct CharBlk4 *blks; +}; + +struct TermBuf NewTermBuf(size_t wid, size_t hgt); + +size_t TermMaxChars(struct TermBuf *term); + +size_t TermBufToChars(struct TermBuf *term, char *buf, size_t max_chars); \ No newline at end of file diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..4edf4da --- /dev/null +++ b/source/main.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "control.h" +#include "fumotris.h" +#include "term.h" +#include "tetr.h" + +#ifdef _WIN32 +#include "win.h" +#endif + +const enum KeyCode key_codes[] = { + LEFT, + RIGHT, + SOFT_DROP, + HARD_DROP, + ROTATE_CCW, + ROTATE_CW, + ROTATE_180, + SWAP, + ESC +}; +const u16f key_binds[] = { + 0x25, + 0x27, + 0x28, + 0x20, + 'Z', + 'X', + 'A', + 'C', + 0x1B +}; + +const enum AxisCode axis_codes[] = { + VSCROLL, + HSCROLL +}; +const u16f axis_binds[] = { 0, 1 }; + +u8 I[16] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 1, 1, 1, 1, + 0, 0, 0, 0 +}; + +void Loop(Ctrl *ctrl) +{ + struct TermBuf term = NewTermBuf(20, 20); + struct CharBlk4 term_blks[term.area]; + memset(term_blks, 0, sizeof(struct CharBlk4) * term.area); + term.blks = term_blks; + + size_t out_max = TermMaxChars(&term); + char out[out_max]; + memset(out, 0, out_max); + + struct TetrMap board = NewTetrMap(10, 20); + u8 board_blks[board.area]; + memset(board_blks, 0, board.area); + board.blks = board_blks; + + struct TetrMap falling = NewTetrMap(4, 4); + u8 falling_blks[falling.area]; + memcpy(falling_blks, I, falling.area); + falling.blks = falling_blks; + + for (int i = 0; i < 7779997; i++) { + TetrMapToTermBuf(&board, &term); + TetrMapToTermBuf(&falling, &term); + + size_t size = TermBufToChars(&term, out, out_max); + //puts(out); + puts("\x1b[H"); + + //falling.y += 1; + + for(int j = 0; j < ctrl->capacity; j++) { + printf("val:%u, is_down:%u, x:%u, y:%u, press:%f, relse:%f\n", + ctrl->bkts[j].axis.data.axis.value, ctrl->bkts[j].axis.data.axis.is_down, + ctrl->bkts[j].axis.data.joystick.x, ctrl->bkts[j].axis.data.joystick.y, + ctrl->bkts[j].axis.last_pressed, ctrl->bkts[j].axis.last_released); + } + /* + struct Axis *axis = CtrlGet(ctrl, LEFT, KEY); + struct Axis *right = CtrlGet(ctrl, RIGHT, KEY); + + printf("ctrl:%u\n", axis); + printf("ctrl:%u\n", right); + + printf("%f\n", axis->last_pressed); + + if (axis->data.axis.is_down) { + printf("left"); + falling.x -= 1; + } + + if (right->data.axis.is_down) { + printf("right"); + falling.x += 1; + }*/ + + WindowsWait(0.1); + } +} + +int main() +{ + WindowsInit(); + + struct ctrl_bkt bkts[16]; + Ctrl ctrl = NewCtrl(bkts, 16); + + for (size_t i = 0; i < 9; i++) { + CtrlMap(&ctrl, key_binds[i], key_codes[i], KEY); + } + for (size_t i = 0; i < 2; i++) { + CtrlMap(&ctrl, axis_codes[i], axis_binds[i], AXIS); + } + CtrlMap(&ctrl, 0, MOUSE, JOYSTICK); + CtrlMap(&ctrl, 0, 0, WINDOW); + + printf("set controls\n"); + + StartInput(&ctrl); + Loop(&ctrl); + + return 0; +} \ No newline at end of file diff --git a/source/test.cpp b/source/test.cpp new file mode 100644 index 0000000..15d0df3 --- /dev/null +++ b/source/test.cpp @@ -0,0 +1,422 @@ +#include +#include +#include +using namespace std; + +void gotoxy(int x, int y); +void setcolor(WORD color); +void setForeGroundAndBackGroundColor(int ForeGroundColor,int BackGroundColor); +void clearscreen(); +void drawpixel( unsigned char x, unsigned char y, unsigned char Color); +void drawpixel2( unsigned char x, unsigned char y, unsigned char Color, char character); +void drawcircle(int x, int y, int a, int b, int color); +void drawline(int x0, int y0, int x1, int y1, int color); +void drawfilledrectangle(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2,unsigned char bkcol); +void drawframe(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,char text_[]); +void drawwindow(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,unsigned char bkcol,char text_[]); + +int main() +{ + gotoxy(1,23); + setcolor(7); + clearscreen(); + + cout<<"click anywhere in console window to write - hello world -\n\n\n\n\n\n\n\n\n\n\n\n\n" + "Press Ctrl+C to Exit"; + + HANDLE hout= GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); + INPUT_RECORD InputRecord; + DWORD Events; + COORD coord; + CONSOLE_CURSOR_INFO cci; + cci.dwSize = 25; + cci.bVisible = FALSE; + SetConsoleCursorInfo(hout, &cci); + SetConsoleMode(hin, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT); + bool EXITGAME = false; + int buttonX=1, buttonY=1; + + drawpixel(buttonX,buttonY ,1); + gotoxy(buttonX+2,buttonY); + setcolor(3); + cout<<"<----- a button \n"; + + + while( !EXITGAME ) + { + + ReadConsoleInput(hin, &InputRecord, 1, &Events); + + + switch ( InputRecord.EventType ){ + case KEY_EVENT: // keyboard input + + + switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) + { + case VK_ESCAPE: + EXITGAME = TRUE; + break; + + case VK_SPACE: + + break; + + + case VK_RETURN: + + break; + + case VK_LEFT: + // left key move player left + cout<<"VK_LEFT = "<= 0) { + ya -= asq * 2; + thresh -= ya; + wy--; + } + + xa += bsq * 2; + wx++; + + if (xa >= ya) + break; + + + drawpixel(x+wx, y-wy, color); + drawpixel(x-wx, y-wy, color); + drawpixel(x+wx, y+wy, color); + drawpixel(x-wx, y+wy, color); + } + + drawpixel(x+a, y, color); + drawpixel(x-a, y, color); + + wx = a; + wy = 0; + xa = bsq * 2 * a; + + ya = 0; + thresh = bsq / 4 - bsq * a; + + for (;;) { + thresh += ya + asq; + + if (thresh >= 0) { + xa -= bsq * 2; + thresh = thresh - xa; + wx--; + } + + ya += asq * 2; + wy++; + + if (ya > xa) + break; + + drawpixel(x+wx, y-wy, color); + drawpixel(x-wx, y-wy, color); + drawpixel(x+wx, y+wy, color); + drawpixel(x-wx, y+wy, color); + } +} + +//***************************************************************************** + +void drawline(int x0, int y0, int x1, int y1, int color){ + int pix = color; + int dy = y1 - y0; + int dx = x1 - x0; + int stepx, stepy; + + if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } + if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } + dy <<= 1; // dy is now 2*dy + dx <<= 1; // dx is now 2*dx + + drawpixel( x0, y0,pix); + if (dx > dy) { + int fraction = dy - (dx >> 1); // same as 2*dy - dx + while (x0 != x1) { + if (fraction >= 0) { + y0 += stepy; + fraction -= dx; // same as fraction -= 2*dx + } + x0 += stepx; + fraction += dy; // same as fraction -= 2*dy + drawpixel( x0, y0,pix); + } + } else { + int fraction = dx - (dy >> 1); + while (y0 != y1) { + if (fraction >= 0) { + x0 += stepx; + fraction -= dy; + } + y0 += stepy; + fraction += dx; + drawpixel( x0, y0,pix); + } + } +} + +//***************************************************************************** + + +void drawframe(unsigned x,unsigned y,unsigned sx,unsigned sy,unsigned char col,unsigned char col2,char text_[]){ + unsigned i,j,m;{ + + m=(sx-x); //differential + j=m/8; //adjust + j=j-1; //more adjustment + gotoxy(x,y);printf("É"); //Top left corner of drawframe + gotoxy(sx,y);printf("»"); //Top right corner of drawframe + gotoxy(x,sy);printf("È"); //Bottom left corner of drawframe + gotoxy(sx,sy);printf("¼"); //Bottom right corner of drawframe + + for (i=x+1;i