File organization, input system work
Still working on the rewrite's input system
This commit is contained in:
parent
0c2653ffaa
commit
d776cf6b46
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
||||||
#FOLDERS
|
#FOLDERS
|
||||||
source/
|
old source/
|
||||||
.vscode/
|
.vscode/
|
|
@ -1,45 +0,0 @@
|
||||||
#include <bit>
|
|
||||||
#include <climits>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
class BinaryEncoder {
|
|
||||||
private:
|
|
||||||
unsigned char *const buffer;
|
|
||||||
std::size_t index;
|
|
||||||
|
|
||||||
public:
|
|
||||||
BinaryEncoder(unsigned char *b) noexcept : buffer(b) {}
|
|
||||||
|
|
||||||
void write(unsigned char val, unsigned char width = 0)
|
|
||||||
{
|
|
||||||
if (width == 0)
|
|
||||||
width = std::bit_width(val);
|
|
||||||
|
|
||||||
const unsigned char bitindex = index % CHAR_BIT;
|
|
||||||
const std::size_t byteindex = index / CHAR_BIT;
|
|
||||||
|
|
||||||
buffer[byteindex] &= compl(UCHAR_MAX << bitindex);
|
|
||||||
buffer[byteindex] |= val << bitindex;
|
|
||||||
|
|
||||||
const unsigned char bitsWritten = CHAR_BIT - bitindex;
|
|
||||||
|
|
||||||
if (width <= bitsWritten) {
|
|
||||||
index += width;
|
|
||||||
} else {
|
|
||||||
index += bitsWritten;
|
|
||||||
this->write(val >> bitsWritten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
unsigned char buf[1];
|
|
||||||
BinaryEncoder be(buf);
|
|
||||||
|
|
||||||
be.write(0b111u);
|
|
||||||
be.write(0b100u);
|
|
||||||
|
|
||||||
if (buf[0] == 39) std::puts("miku");
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
longest.exe
BIN
longest.exe
Binary file not shown.
|
@ -1,74 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct ascii_set {
|
|
||||||
unsigned long long lo;
|
|
||||||
unsigned long long hi;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ascii_set_insert(struct ascii_set *set, char ch)
|
|
||||||
{
|
|
||||||
unsigned long long mask = 1ULL << (ch % 64);
|
|
||||||
|
|
||||||
if (ch < 64) {
|
|
||||||
if (set->lo & mask) {
|
|
||||||
set->lo = mask;
|
|
||||||
set->hi = 0;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
set->lo |= mask;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (set->hi & mask) {
|
|
||||||
set->hi = mask;
|
|
||||||
set->lo = 0;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
set->hi |= mask;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned longest_no_repeat(char *str, unsigned len)
|
|
||||||
{
|
|
||||||
struct ascii_set set = {0};
|
|
||||||
|
|
||||||
unsigned longest = 0;
|
|
||||||
unsigned current = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < len; i++) {
|
|
||||||
if (ascii_set_insert(&set, str[i])) {
|
|
||||||
current++;
|
|
||||||
} else {
|
|
||||||
if (current > longest)
|
|
||||||
longest = current;
|
|
||||||
|
|
||||||
current = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current > longest)
|
|
||||||
longest = current;
|
|
||||||
|
|
||||||
return longest;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
char buf[2048];
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
fputs("enter a string nya: ", stdout);
|
|
||||||
fgets(buf, 2048, stdin);
|
|
||||||
unsigned len = strlen(buf);
|
|
||||||
buf[--len] = '\0';
|
|
||||||
|
|
||||||
unsigned longest = longest_no_repeat(buf, len);
|
|
||||||
printf("longest without repeating is %u nya\n", longest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#include "allocator.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
void *Alloc(struct Pool *pool, usize n, usize size)
|
|
||||||
{
|
|
||||||
void *ptr = (u8 *)pool->memory + pool->allocated;
|
|
||||||
|
|
||||||
pool->allocated += n * size;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Pool {
|
|
||||||
void *memory;
|
|
||||||
usize size;
|
|
||||||
usize allocated;
|
|
||||||
};
|
|
|
@ -1,45 +0,0 @@
|
||||||
#include "controller.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateController(struct Controller *ctrl, struct Pool *pool, usize axes)
|
|
||||||
{
|
|
||||||
*ctrl = (struct Controller) {
|
|
||||||
.map.v_codes = calloc(16, sizeof(u16)),
|
|
||||||
.map.v_bind_ptrs = calloc(16, sizeof(struct InputVBind *)),
|
|
||||||
|
|
||||||
.map.n = 16,
|
|
||||||
|
|
||||||
.v_axes = calloc(16, sizeof(struct InputVAxis)),
|
|
||||||
.v_binds = calloc(16, sizeof(struct InputVBind)),
|
|
||||||
|
|
||||||
.v_axes_n = 16,
|
|
||||||
.v_binds_n = 16
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 as_v_code(u16 scan_code, u8 type)
|
|
||||||
{
|
|
||||||
return (u32)scan_code | ((u32)type << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControllerBindV(struct Controller *ctrl, u16 v_bind, u16 v_axis)
|
|
||||||
{
|
|
||||||
ctrl->v_binds[v_bind].v_axis = &ctrl->v_axes[v_axis];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControllerBind(struct Controller *ctrl, u16 scan_code, u8 type, u16 v_bind)
|
|
||||||
{
|
|
||||||
u32 v_code = as_v_code(scan_code, type);
|
|
||||||
|
|
||||||
struct InputVBind *v_bind = &ctrl->v_binds[v_bind];
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControllerPoll(struct Controller *ctrl, struct InputRecord *recs, usize n)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < n; i++) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Controller {
|
|
||||||
struct {
|
|
||||||
u32 *v_codes;
|
|
||||||
struct InputVBind **v_bind_ptrs;
|
|
||||||
|
|
||||||
usize n;
|
|
||||||
} map;
|
|
||||||
|
|
||||||
struct InputVAxis *v_axes;
|
|
||||||
struct InputVBind *v_binds;
|
|
||||||
|
|
||||||
usize v_axes_n;
|
|
||||||
usize v_binds_n;
|
|
||||||
};
|
|
|
@ -1,72 +0,0 @@
|
||||||
#include "dictionary.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
void *index_bkt(struct Dictionary *dict, usize i)
|
|
||||||
{
|
|
||||||
return (u8 *)dict->bkts + i * dict->bkt_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *get_key(struct Dictionary *dict, void *bkt)
|
|
||||||
{
|
|
||||||
return (u32 *)bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *get_value_ptr(struct Dictionary *dict, void *bkt)
|
|
||||||
{
|
|
||||||
return (u8 *)bkt + dict->value_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_bkt(struct Dictionary *dict, void *bkt, u32 key, void *value_ptr)
|
|
||||||
{
|
|
||||||
*get_key(dict, bkt) = key;
|
|
||||||
memcpy(get_value_ptr(dict, bkt), value_ptr, dict->value_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *probe_bkt(struct Dictionary *dict, usize index, u32 key)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < dict->capacity; i++) {
|
|
||||||
void *bkt = index_bkt(dict, (index + i) % dict->capacity);
|
|
||||||
|
|
||||||
if (*get_key(dict, bkt) == key)
|
|
||||||
return bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *probe_empty_bkt(struct Dictionary *dict, usize index, u32 key)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < dict->capacity; i++) {
|
|
||||||
void *bkt = index_bkt(dict, (index + i) % dict->capacity);
|
|
||||||
|
|
||||||
u32 k = *get_key(dict, bkt);
|
|
||||||
if (k == 0 or k == key)
|
|
||||||
return bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *DictionaryFind(struct Dictionary *dict, u32 key)
|
|
||||||
{
|
|
||||||
usize index = key % dict->capacity;
|
|
||||||
|
|
||||||
void *bkt = probe_bkt(dict, index, key);
|
|
||||||
if (bkt == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return get_value_ptr(dict, bkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *DictionarySet(struct Dictionary *dict, u32 key, void *value_ptr)
|
|
||||||
{
|
|
||||||
usize index = key % dict->capacity;
|
|
||||||
|
|
||||||
void *bkt = probe_empty_bkt(dict, index, key);
|
|
||||||
|
|
||||||
if (*get_key(dict, bkt) == 0)
|
|
||||||
set_bkt(dict, bkt, key, value_ptr);
|
|
||||||
|
|
||||||
return bkt;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct DictionaryTemplate {
|
|
||||||
u32 *keys;
|
|
||||||
void **value_ptrs;
|
|
||||||
|
|
||||||
usize n;
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "allocator.h"
|
|
||||||
#include "controller.h"
|
|
||||||
|
|
||||||
#define ENGINE_HEAP_SIZE 262144
|
|
||||||
|
|
||||||
|
|
||||||
struct Instance {
|
|
||||||
struct Controller ctrl;
|
|
||||||
|
|
||||||
nsec time;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CreateInstance(struct Instance *inst, void *heap, usize heap_size)
|
|
||||||
{
|
|
||||||
CreateController(&inst->ctrl, &inst->pool, 16);
|
|
||||||
|
|
||||||
struct Controller ctrl;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
|
|
||||||
void *input_worker(void *hand_arg)
|
|
||||||
{
|
|
||||||
while(true) {
|
|
||||||
PlatformReadInput();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
enum InputType {
|
|
||||||
BUTTON,
|
|
||||||
AXIS,
|
|
||||||
JOYSTICK
|
|
||||||
};
|
|
||||||
|
|
||||||
union InputData {
|
|
||||||
struct Button {
|
|
||||||
u64 value;
|
|
||||||
} but;
|
|
||||||
|
|
||||||
struct Axis {
|
|
||||||
i64 value;
|
|
||||||
} axis;
|
|
||||||
|
|
||||||
struct Joystick {
|
|
||||||
i32 x;
|
|
||||||
i32 y;
|
|
||||||
} js;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputRecord {
|
|
||||||
union InputData data;
|
|
||||||
|
|
||||||
nsec time;
|
|
||||||
|
|
||||||
u16 scan_code;
|
|
||||||
|
|
||||||
u8 type;
|
|
||||||
bool is_down;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputVAxis {
|
|
||||||
union InputData data;
|
|
||||||
|
|
||||||
nsec last_pressed;
|
|
||||||
nsec last_released;
|
|
||||||
|
|
||||||
u8 type;
|
|
||||||
bool is_down;
|
|
||||||
bool is_held;
|
|
||||||
bool is_up;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct InputVBind;
|
|
||||||
|
|
||||||
struct InputVBind {
|
|
||||||
struct InputVBind *link;
|
|
||||||
|
|
||||||
struct InputVAxis *v_axis;
|
|
||||||
union InputData relationship;
|
|
||||||
};
|
|
|
@ -1,118 +0,0 @@
|
||||||
#include "terminal.h"
|
|
||||||
|
|
||||||
#define RESET_STR_SIZE 7
|
|
||||||
#define COLOR4_MAX_SIZE 10
|
|
||||||
|
|
||||||
|
|
||||||
usize max_out(usize wid, usize hgt)
|
|
||||||
{
|
|
||||||
return RESET_STR_SIZE
|
|
||||||
+ wid * hgt * COLOR4_MAX_SIZE
|
|
||||||
+ wid * hgt * UTF8_MAX_SIZE
|
|
||||||
+ hgt
|
|
||||||
+ 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateTerminal(Alloc *pool, struct Terminal *term, usize wid, usize hgt)
|
|
||||||
{
|
|
||||||
term->col4s = pool->alloc(wid * hgt, sizeof(struct Color4));
|
|
||||||
term->utf8s = pool->alloc(wid * hgt, UTF8_MAX_SIZE);
|
|
||||||
term->out = pool->alloc(wid * hgt, max_out(wid, hgt));
|
|
||||||
|
|
||||||
if (pool->oom)
|
|
||||||
return;
|
|
||||||
|
|
||||||
term->wid = wid;
|
|
||||||
term->hgt = hgt;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize u8_to_str(char *out, u8 x)
|
|
||||||
{
|
|
||||||
u8 ones = x % 10;
|
|
||||||
u8 tens = x / 10;
|
|
||||||
u8 hnds = tens / 10;
|
|
||||||
tens %= 10;
|
|
||||||
|
|
||||||
usize len = 0;
|
|
||||||
|
|
||||||
out[len] = hnds + 48;
|
|
||||||
len += (hnds != 0);
|
|
||||||
|
|
||||||
out[len] = tens + 48;
|
|
||||||
len += (hnds | tens) != 0;
|
|
||||||
|
|
||||||
out[len++] = ones + 48;
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 ansi_bg(u8 bg)
|
|
||||||
{
|
|
||||||
return bg + (bg < 8 ? 40 : 92);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 ansi_fg(u8 fg)
|
|
||||||
{
|
|
||||||
return fg + (fg < 8 ? 30 : 82);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize col4_dif_to_str(char *out, struct Color4 *dif, struct Color4 *col)
|
|
||||||
{
|
|
||||||
if (*(u8 *)dif == *(u8 *)col)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
usize len = 0;
|
|
||||||
|
|
||||||
out[len++] = '\x1b';
|
|
||||||
out[len++] = '[';
|
|
||||||
|
|
||||||
if (dif->bg != col->bg) {
|
|
||||||
len += u8_to_str(out + len, ansi_bg(col->bg));
|
|
||||||
|
|
||||||
if (dif->fg != col->fg) {
|
|
||||||
out[len++] += ';';
|
|
||||||
len += u8_to_str(out + len, ansi_fg(col->fg));
|
|
||||||
}
|
|
||||||
} else if (dif->fg != col->fg) {
|
|
||||||
len += u8_to_str(out + len, ansi_fg(col->fg));
|
|
||||||
}
|
|
||||||
|
|
||||||
out[len++] = 'm';
|
|
||||||
*dif = *col;
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize utf8_to_str(char *out, char utf8[4])
|
|
||||||
{
|
|
||||||
if (utf8[0] == 0)
|
|
||||||
utf8[0] = '#';
|
|
||||||
|
|
||||||
out = utf8;
|
|
||||||
return 1 + (utf8[1] != 0) + (utf8[2] != 0) + (utf8[3] != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize TerminalPrint(struct Terminal *term)
|
|
||||||
{
|
|
||||||
term->out = (char[3]) { '\x1b', '[', 'H' };
|
|
||||||
usize len = 3;
|
|
||||||
|
|
||||||
struct Color4 dif = {0};
|
|
||||||
|
|
||||||
char (*utf8)[4] = term->utf8s;
|
|
||||||
struct Color4 *col4 = term->col4s;
|
|
||||||
|
|
||||||
for (usize y = 0; y < term->hgt; y++) {
|
|
||||||
for (usize x = 0; x < term->wid; x++, utf8++, col4++) {
|
|
||||||
len += col4_dif_to_str(term->out + len, &dif, col4);
|
|
||||||
len += utf8_to_str(term->out + len, *utf8);
|
|
||||||
}
|
|
||||||
term->out[len++] = '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
*(term->out + len) = (char[4]) { '\x1b', '[', '0', 'm' };
|
|
||||||
len += 4;
|
|
||||||
|
|
||||||
term->out[len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "allocator.h"
|
|
||||||
|
|
||||||
#define UTF8_MAX_SIZE 4
|
|
||||||
|
|
||||||
|
|
||||||
struct Color4 {
|
|
||||||
u8 bg : 4;
|
|
||||||
u8 fg : 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Terminal {
|
|
||||||
struct Color4 *col4s;
|
|
||||||
char (*utf8s)[UTF8_MAX_SIZE];
|
|
||||||
|
|
||||||
char *out;
|
|
||||||
|
|
||||||
usize wid;
|
|
||||||
usize hgt;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void CreateTerminal(Alloc *pool, struct Terminal *term, usize wid, usize hgt);
|
|
||||||
|
|
||||||
usize TerminalPrint(struct Terminal *term);
|
|
|
@ -1,86 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const fumo = @import("fumostd.zig");
|
|
||||||
const input = @import("input.zig");
|
|
||||||
|
|
||||||
const Axis = struct {
|
|
||||||
data: f64 = 0,
|
|
||||||
|
|
||||||
last_pressed: u64 = 0,
|
|
||||||
last_released: u64 = 0,
|
|
||||||
|
|
||||||
is_down: bool = false,
|
|
||||||
is_held: bool = false,
|
|
||||||
is_up: bool = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Bind = struct {
|
|
||||||
axis: *Axis,
|
|
||||||
multiplier: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Controller = struct {
|
|
||||||
const BindHashMap = fumo.HashMap(*Bind);
|
|
||||||
|
|
||||||
axes: []Axis,
|
|
||||||
binds: []Bind,
|
|
||||||
|
|
||||||
code_map: BindHashMap,
|
|
||||||
|
|
||||||
pending: [16]*Axis = undefined,
|
|
||||||
n_pending: usize = 0,
|
|
||||||
|
|
||||||
pub fn init(
|
|
||||||
arena: std.mem.Allocator,
|
|
||||||
n_axes: usize,
|
|
||||||
n_binds: usize,
|
|
||||||
n_codes: usize,
|
|
||||||
) !@This() {
|
|
||||||
const ctrl = @This(){
|
|
||||||
.axes = try arena.alloc(Axis, n_axes),
|
|
||||||
.binds = try arena.alloc(Bind, n_binds),
|
|
||||||
|
|
||||||
.code_map = try BindHashMap.init(arena, n_codes),
|
|
||||||
};
|
|
||||||
|
|
||||||
@memset(ctrl.axes, Axis{});
|
|
||||||
|
|
||||||
return ctrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bindCode(this: *@This(), code: usize, bind: usize) !void {
|
|
||||||
try this.code_map.set(code, &this.binds[bind]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bindAxis(this: *@This(), bind: usize, axis: usize, mul: f64) void {
|
|
||||||
this.binds[bind] = Bind{
|
|
||||||
.axis = &this.axes[axis],
|
|
||||||
.multiplier = mul,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll(this: *@This(), rec_circ: *input.RecordCircle) void {
|
|
||||||
for (this.pending[0..this.n_pending]) |axis| {
|
|
||||||
axis.is_up = false;
|
|
||||||
axis.is_down = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for (rec_circ.read()) |slice| for (slice) |record| {
|
|
||||||
if (this.code_map.find(record.code)) |bind| {
|
|
||||||
dispatch(bind, record);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch(bind: *Bind, record: input.Record) void {
|
|
||||||
bind.axis.data = record.data * bind.multiplier;
|
|
||||||
|
|
||||||
bind.axis.is_down = record.data != 0;
|
|
||||||
bind.axis.is_up = record.data == 0;
|
|
||||||
|
|
||||||
if (record.data != 0) {
|
|
||||||
bind.axis.last_pressed = record.time;
|
|
||||||
} else {
|
|
||||||
bind.axis.last_released = record.time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -82,16 +82,16 @@ pub fn HashMap(comptime V: type) type {
|
||||||
pub fn Buffer(comptime T: type, comptime size: comptime_int) type {
|
pub fn Buffer(comptime T: type, comptime size: comptime_int) type {
|
||||||
return struct {
|
return struct {
|
||||||
array: [size]T = undefined,
|
array: [size]T = undefined,
|
||||||
len: usize = 0,
|
written: usize = 0,
|
||||||
read: usize = 0,
|
used: usize = 0,
|
||||||
|
|
||||||
pub fn slice(this: *@This()) []T {
|
|
||||||
return this.array[this.read..this.len];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flush(this: *@This()) void {
|
pub fn flush(this: *@This()) void {
|
||||||
this.len = 0;
|
this.written = 0;
|
||||||
this.read = 0;
|
this.used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSlice(this: *@This()) []T {
|
||||||
|
return this.array[this.used..this.written];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,9 @@ pub fn CircleBuffer(comptime T: type, comptime size: comptime_int) type {
|
||||||
read: usize = 0,
|
read: usize = 0,
|
||||||
write: usize = 0,
|
write: usize = 0,
|
||||||
|
|
||||||
|
cond: std.Thread.Condition = .{},
|
||||||
|
mutex: std.Thread.Mutex = .{},
|
||||||
|
|
||||||
pub fn write(this: *@This(), slice: []T) usize {
|
pub fn write(this: *@This(), slice: []T) usize {
|
||||||
const read_atm: usize = @atomicLoad(usize, &this.read, AtomicOrder.acquire);
|
const read_atm: usize = @atomicLoad(usize, &this.read, AtomicOrder.acquire);
|
||||||
const contiguous: bool = this.write < read_atm;
|
const contiguous: bool = this.write < read_atm;
|
||||||
|
@ -137,5 +140,11 @@ pub fn CircleBuffer(comptime T: type, comptime size: comptime_int) type {
|
||||||
@atomicStore(usize, &this.read, (this.read + read_total) % size, AtomicOrder.release);
|
@atomicStore(usize, &this.read, (this.read + read_total) % size, AtomicOrder.release);
|
||||||
return struct { region_a, region_b };
|
return struct { region_a, region_b };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn waitRead(this: *@This()) void {
|
||||||
|
this.mutex.lock();
|
||||||
|
this.cond.wait(&this.mutex);
|
||||||
|
this.mutex.unlock();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ const fumo = @import("fumostd.zig");
|
||||||
const platform = @import("platform.zig");
|
const platform = @import("platform.zig");
|
||||||
|
|
||||||
pub const RecordCircle = fumo.CircleBuffer(Record, 16);
|
pub const RecordCircle = fumo.CircleBuffer(Record, 16);
|
||||||
pub const StringCircle = fumo.CircleBuffer(u8, 16 * 4);
|
|
||||||
|
|
||||||
pub const RecordBuffer = fumo.Buffer(Record, 16);
|
pub const RecordBuffer = fumo.Buffer(Record, 16);
|
||||||
pub const StringBuffer = fumo.Buffer(u8, 16 * 4);
|
pub const StringBuffer = fumo.Buffer(u8, 16 * 4);
|
||||||
|
@ -12,20 +11,20 @@ pub const Record = struct {
|
||||||
const Type = enum { axis, joystick };
|
const Type = enum { axis, joystick };
|
||||||
|
|
||||||
hash: usize,
|
hash: usize,
|
||||||
data: f64,
|
value: f64,
|
||||||
|
|
||||||
pub fn readAxis(buf: RecordBuffer, code: usize, data: f64) void {
|
pub fn readAxis(buf: RecordBuffer, code: usize, data: f64) void {
|
||||||
buf.array[buf.len] = Record{
|
buf.array[buf.written] = Record{
|
||||||
.hash = hashCode(code, Type.axis),
|
.hash = hashCode(code, Type.axis),
|
||||||
.data = data,
|
.data = data,
|
||||||
};
|
};
|
||||||
buf.len += 1;
|
buf.written += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readJoystick(buf: RecordBuffer, code: usize, x: f64, y: f64) void {
|
pub fn readJoystick(buf: RecordBuffer, code: usize, x: f64, y: f64) void {
|
||||||
buf.array[buf.len] = .{ .code = scancode | Record.joystick_ofs, .data = x };
|
buf.array[buf.written] = .{ .code = hashCode(code, Type.joystick), .data = x };
|
||||||
buf.array[buf.len + 1] = .{ .code = scancode | Record.joystick_ofs, .data = y };
|
buf.array[buf.written + 1] = .{ .code = hashCode(code, Type.joystick), .data = y };
|
||||||
buf.len += 2;
|
buf.written += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hashCode(code: usize, code_type: usize) usize {
|
fn hashCode(code: usize, code_type: usize) usize {
|
||||||
|
@ -35,10 +34,10 @@ pub const Record = struct {
|
||||||
|
|
||||||
pub const Handle = struct {
|
pub const Handle = struct {
|
||||||
rec_circ: RecordCircle = .{},
|
rec_circ: RecordCircle = .{},
|
||||||
str_circ: StringCircle = .{},
|
|
||||||
|
|
||||||
thread: std.Thread = undefined,
|
thread: std.Thread = undefined,
|
||||||
signal: std.Thread.Condition = .{},
|
cond: std.Thread.Condition = .{},
|
||||||
|
mutex: std.Thread.Mutex = .{},
|
||||||
|
|
||||||
pub fn init() !Handle {
|
pub fn init() !Handle {
|
||||||
var handle = Handle{};
|
var handle = Handle{};
|
||||||
|
@ -48,21 +47,98 @@ pub const Handle = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker(hand: *Handle) !void {
|
fn worker(hand: *Handle) !void {
|
||||||
var rec_buf = RecordBuffer{};
|
var new_recs = RecordBuffer{};
|
||||||
var str_buf = StringBuffer{};
|
|
||||||
|
|
||||||
while (true) : ({
|
while (true) : (new_recs.flush()) {
|
||||||
rec_buf.flush();
|
try platform.ReadInput(&new_recs, &str_buf);
|
||||||
str_buf.flush();
|
|
||||||
}) {
|
|
||||||
try platform.ReadInput(&rec_buf, &str_buf);
|
|
||||||
|
|
||||||
while (rec_buf.read < rec_buf.len) : ({
|
while (new_recs.used < new_recs.written) : (rec_circ.waitRead()) {
|
||||||
hand.signal.wait(&hand.mutex);
|
new_recs.used += hand.rec_circ.write(new_recs.getSlice());
|
||||||
}) {
|
|
||||||
rec_buf.read += hand.rec_circ.write(rec_buf.slice());
|
|
||||||
str_buf.read += hand.str_circ.write(str_buf.slice());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Controller = struct {
|
||||||
|
const CodeBindMap = fumo.HashMap(*Bind);
|
||||||
|
|
||||||
|
axes: []Axis,
|
||||||
|
binds: []Bind,
|
||||||
|
map: CodeBindMap,
|
||||||
|
|
||||||
|
pending: [16]*Axis = undefined,
|
||||||
|
pending_len: usize = 0,
|
||||||
|
|
||||||
|
const Axis = struct {
|
||||||
|
value: f64 = 0,
|
||||||
|
|
||||||
|
last_pressed: u64 = 0,
|
||||||
|
last_released: u64 = 0,
|
||||||
|
|
||||||
|
is_down: bool = false,
|
||||||
|
is_held: bool = false,
|
||||||
|
is_up: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Bind = struct {
|
||||||
|
axis: *Axis,
|
||||||
|
multiplier: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
arena: std.mem.Allocator,
|
||||||
|
axes: usize,
|
||||||
|
binds: usize,
|
||||||
|
codes: usize,
|
||||||
|
) !Controller {
|
||||||
|
const ctrl = Controller{
|
||||||
|
.axes = try arena.alloc(Axis, axes),
|
||||||
|
.binds = try arena.alloc(Bind, binds),
|
||||||
|
|
||||||
|
.code_map = try CodeBindMap.init(arena, codes),
|
||||||
|
};
|
||||||
|
|
||||||
|
@memset(ctrl.axes, Axis{});
|
||||||
|
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapCodeToBind(ctrl: *Controller, code: usize, bind: usize) !void {
|
||||||
|
try ctrl.map.set(code, &ctrl.binds[bind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapBindToAxis(ctrl: *Controller, bind: usize, axis: usize, multiplier: f64) void {
|
||||||
|
ctrl.binds[bind] = Bind{
|
||||||
|
.axis = ctrl.axes + axis,
|
||||||
|
.multiplier = multiplier,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll(ctrl: *Controller, rec_circ: *RecordCircle) void {
|
||||||
|
for (ctrl.pending[0..ctrl.pending_len]) |axis| {
|
||||||
|
axis.is_up = false;
|
||||||
|
axis.is_down = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (rec_circ.read()) |slice| for (slice) |record| {
|
||||||
|
if (ctrl.map.find(record.hash)) |bind| {
|
||||||
|
std.debug.print("{}\n", .{record.hash});
|
||||||
|
|
||||||
|
dispatch(bind, record);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch(bind: *Bind, record: Record) void {
|
||||||
|
bind.axis.value = record.value * bind.multiplier;
|
||||||
|
|
||||||
|
bind.axis.is_down = record.value != 0;
|
||||||
|
bind.axis.is_up = record.value == 0;
|
||||||
|
|
||||||
|
if (record.value != 0) {
|
||||||
|
bind.axis.last_pressed = record.time;
|
||||||
|
} else {
|
||||||
|
bind.axis.last_released = record.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fumo = @import("fumostd.zig");
|
const fumo = @import("fumostd.zig");
|
||||||
const controller = @import("controller.zig");
|
|
||||||
const input = @import("input.zig");
|
const input = @import("input.zig");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
var ctrl = try controller.Controller.init(allocator, 3, 3, 3);
|
var ctrl = try input.Controller.init(allocator, 3, 3, 3);
|
||||||
|
|
||||||
try ctrl.bindCode(69, 0);
|
try ctrl.mapCodeToBind(69, 0);
|
||||||
ctrl.bindAxis(0, 2, 1);
|
ctrl.mapBindToAxis(0, 2, 1);
|
||||||
|
|
||||||
var hand = try input.Handle.init();
|
var hand = try input.Handle.init();
|
||||||
hand.aquire();
|
|
||||||
|
|
||||||
_ = hand.rec_circ.write(input.Record.readAxis(69, 123));
|
|
||||||
|
|
||||||
|
while (true) {
|
||||||
ctrl.poll(&hand.rec_circ);
|
ctrl.poll(&hand.rec_circ);
|
||||||
|
hand.cond.signal();
|
||||||
std.debug.print("axis index 2: {}", .{ctrl.axes[2].data});
|
}
|
||||||
|
|
||||||
hand.release();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,8 @@ fn translate(
|
||||||
if (!win_rec.Event.KeyEvent.bKeyDown)
|
if (!win_rec.Event.KeyEvent.bKeyDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
str_buf.len += sprintWchar(
|
str_buf.written += sprintWchar(
|
||||||
str_buf.array + str_buf.len,
|
str_buf.array + str_buf.written,
|
||||||
win_rec.Event.KeyEvent.uChar.UnicodeChar,
|
win_rec.Event.KeyEvent.uChar.UnicodeChar,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
usize min(usize a, usize b)
|
|
||||||
{
|
|
||||||
return a < b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize max(usize a, usize b)
|
|
||||||
{
|
|
||||||
return a > b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsec TimeNow()
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
timespec_get(&ts, TIME_UTC);
|
|
||||||
|
|
||||||
return ts.tv_nsec + ts.tv_sec * ONE_E_9;
|
|
||||||
}
|
|
||||||
|
|
||||||
double TimeNowD()
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
timespec_get(&ts, TIME_UTC);
|
|
||||||
|
|
||||||
return ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Hash(void *item, usize size)
|
|
||||||
{
|
|
||||||
u8 *data = (u8 *)item;
|
|
||||||
|
|
||||||
u32 h = 98317;
|
|
||||||
for (usize i = 0; i < size; i++) {
|
|
||||||
h ^= data[i];
|
|
||||||
h *= 0x5bd1e995;
|
|
||||||
h ^= h >> 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <iso646.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define nullptr ((void *)0)
|
|
||||||
|
|
||||||
#define ONE_E_9 1000000000
|
|
||||||
|
|
||||||
|
|
||||||
typedef size_t usize;
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
typedef int8_t i8;
|
|
||||||
typedef int_fast8_t i8f;
|
|
||||||
|
|
||||||
typedef int16_t i16;
|
|
||||||
typedef int_fast16_t i16f;
|
|
||||||
|
|
||||||
typedef int32_t i32;
|
|
||||||
typedef int_fast32_t i32f;
|
|
||||||
|
|
||||||
typedef int64_t i64;
|
|
||||||
typedef int_fast64_t i64f;
|
|
||||||
|
|
||||||
|
|
||||||
typedef u64 nsec;
|
|
||||||
|
|
||||||
|
|
||||||
size_t min(usize a, usize b);
|
|
||||||
size_t max(usize a, usize b);
|
|
||||||
|
|
||||||
nsec TimeNow();
|
|
||||||
double TimeNowD();
|
|
||||||
|
|
||||||
u32 Hash(void *item, usize size);
|
|
|
@ -1,92 +0,0 @@
|
||||||
#include "fumoengine.h"
|
|
||||||
#include "platform.h"
|
|
||||||
|
|
||||||
|
|
||||||
void Panic(char *message)
|
|
||||||
{
|
|
||||||
printf(message);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CoroutineAdd(struct Instance *inst, void *state, coroutine_handler callback)
|
|
||||||
{
|
|
||||||
return VectorAdd(&inst->coroutines, &(struct Coroutine) {
|
|
||||||
.callback = callback,
|
|
||||||
.state = state,
|
|
||||||
.next_scheduled = inst->time
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoroutineTryInvoke(struct Instance *inst, struct Coroutine *co)
|
|
||||||
{
|
|
||||||
while (inst->time > co->next_scheduled) {
|
|
||||||
nsec wait = co->callback(inst, co->state);
|
|
||||||
if (wait == 0) {
|
|
||||||
co->next_scheduled = inst->time;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
co->next_scheduled += wait;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateFumoInstance(struct Instance *instance)
|
|
||||||
{
|
|
||||||
if (!PlatformInit())
|
|
||||||
Panic("Platform failed to initialize");
|
|
||||||
|
|
||||||
if (!CreateController(&instance->ctrl))
|
|
||||||
Panic("Out of memory");
|
|
||||||
|
|
||||||
if (!CreateInputThread(&instance->input_hand))
|
|
||||||
Panic("Input handle failed to initialize");
|
|
||||||
|
|
||||||
if (!CreateEvent(&instance->on_start))
|
|
||||||
Panic("Out of memory");
|
|
||||||
|
|
||||||
if (!CreateEvent(&instance->on_update))
|
|
||||||
Panic("Out of memory");
|
|
||||||
|
|
||||||
if (!CreateEvent(&instance->on_draw))
|
|
||||||
Panic("Out of memory");
|
|
||||||
|
|
||||||
if (!CreateVector(&instance->coroutines, sizeof(struct Coroutine)))
|
|
||||||
Panic("Out of memory");
|
|
||||||
|
|
||||||
instance->time = TimeNow();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FumoInstanceRun(struct Instance *inst)
|
|
||||||
{
|
|
||||||
EventInvoke(&inst->on_start, inst);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// Time
|
|
||||||
nsec now = TimeNow();
|
|
||||||
inst->frametime = now - inst->time;
|
|
||||||
inst->time = now;
|
|
||||||
|
|
||||||
// Input
|
|
||||||
if (!InputAquire(&inst->input_hand))
|
|
||||||
Panic("Aquire failed");
|
|
||||||
|
|
||||||
ControllerPoll(&inst->ctrl, &inst->input_hand.recs);
|
|
||||||
|
|
||||||
if (!InputRelease(&inst->input_hand))
|
|
||||||
Panic("Release failed");
|
|
||||||
|
|
||||||
// Update
|
|
||||||
EventInvoke(&inst->on_update, inst);
|
|
||||||
|
|
||||||
for (usize i = 0; i < inst->coroutines.len; i++) {
|
|
||||||
CoroutineTryInvoke(inst, VectorGet(&inst->coroutines, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
EventInvoke(&inst->on_draw, inst);
|
|
||||||
|
|
||||||
//_sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "ctrl.h"
|
|
||||||
#include "event.h"
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef nsec (*coroutine_handler)(void *state, void *instance);
|
|
||||||
|
|
||||||
|
|
||||||
struct Coroutine {
|
|
||||||
coroutine_handler callback;
|
|
||||||
void *state;
|
|
||||||
nsec next_scheduled;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Instance {
|
|
||||||
struct Controller ctrl;
|
|
||||||
struct InputHandle input_hand;
|
|
||||||
|
|
||||||
struct Event on_start;
|
|
||||||
struct Event on_update;
|
|
||||||
struct Event on_draw;
|
|
||||||
|
|
||||||
struct Vector coroutines;
|
|
||||||
|
|
||||||
nsec time;
|
|
||||||
nsec frametime;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Panic(char *message);
|
|
||||||
|
|
||||||
bool CoroutineAdd(struct Instance *inst, void *state, coroutine_handler callback);
|
|
||||||
|
|
||||||
void CoroutineTryInvoke(struct Instance *inst, struct Coroutine *co);
|
|
||||||
|
|
||||||
bool CreateFumoInstance(struct Instance *game);
|
|
||||||
|
|
||||||
bool FumoInstanceRun(struct Instance *game);
|
|
|
@ -1,98 +0,0 @@
|
||||||
#include "dictionary.h"
|
|
||||||
#include <stdalign.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateDictionary(struct Dictionary *dict, usize value_size)
|
|
||||||
{
|
|
||||||
void *bkts = calloc(16, value_size);
|
|
||||||
|
|
||||||
if (bkts == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dict->value_size = value_size;
|
|
||||||
dict->value_offset = max(alignof(u32), alignof(u8[dict->value_size]));
|
|
||||||
dict->bkt_size = dict->value_offset + value_size;
|
|
||||||
|
|
||||||
dict->filled = 0;
|
|
||||||
dict->capacity = 16;
|
|
||||||
dict->bkts = bkts;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeDictionary(struct Dictionary *dict)
|
|
||||||
{
|
|
||||||
free(dict->bkts);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *index_bkt(struct Dictionary *dict, usize i)
|
|
||||||
{
|
|
||||||
return (u8 *)dict->bkts + i * dict->bkt_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *get_key(struct Dictionary *dict, void *bkt)
|
|
||||||
{
|
|
||||||
return (u32 *)bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *get_value_ptr(struct Dictionary *dict, void *bkt)
|
|
||||||
{
|
|
||||||
return (u8 *)bkt + dict->value_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_bkt(struct Dictionary *dict, void *bkt, u32 key, void *value_ptr)
|
|
||||||
{
|
|
||||||
*get_key(dict, bkt) = key;
|
|
||||||
memcpy(get_value_ptr(dict, bkt), value_ptr, dict->value_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *probe_bkt(struct Dictionary *dict, usize index, u32 key)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < dict->capacity; i++) {
|
|
||||||
void *bkt = index_bkt(dict, (index + i) % dict->capacity);
|
|
||||||
|
|
||||||
if (*get_key(dict, bkt) == key)
|
|
||||||
return bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *probe_empty_bkt(struct Dictionary *dict, usize index, u32 key)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < dict->capacity; i++) {
|
|
||||||
void *bkt = index_bkt(dict, (index + i) % dict->capacity);
|
|
||||||
|
|
||||||
u32 k = *get_key(dict, bkt);
|
|
||||||
if (k == 0 or k == key)
|
|
||||||
return bkt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *DictionaryFind(struct Dictionary *dict, u32 key)
|
|
||||||
{
|
|
||||||
usize index = key % dict->capacity;
|
|
||||||
|
|
||||||
void *bkt = probe_bkt(dict, index, key);
|
|
||||||
if (bkt == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return get_value_ptr(dict, bkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *DictionarySet(struct Dictionary *dict, u32 key, void *value_ptr)
|
|
||||||
{
|
|
||||||
usize index = key % dict->capacity;
|
|
||||||
|
|
||||||
void *bkt = probe_empty_bkt(dict, index, key);
|
|
||||||
|
|
||||||
if (*get_key(dict, bkt) == 0)
|
|
||||||
set_bkt(dict, bkt, key, value_ptr);
|
|
||||||
|
|
||||||
return bkt;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Dictionary {
|
|
||||||
usize value_size;
|
|
||||||
usize value_offset;
|
|
||||||
usize bkt_size;
|
|
||||||
|
|
||||||
usize filled;
|
|
||||||
usize capacity;
|
|
||||||
void *bkts;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CreateDictionary(struct Dictionary *dict, usize value_size);
|
|
||||||
|
|
||||||
void FreeDictionary(struct Dictionary *dict);
|
|
||||||
|
|
||||||
void *DictionaryFind(struct Dictionary *dict, u32 key);
|
|
||||||
|
|
||||||
void *DictionarySet(struct Dictionary *dict, u32 key, void *value_ptr);
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include "event.h"
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateEvent(struct Event *event)
|
|
||||||
{
|
|
||||||
struct Method *methods = malloc(16 * sizeof(struct Method));
|
|
||||||
|
|
||||||
if (methods == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*event = (struct Event) {
|
|
||||||
.methods = methods,
|
|
||||||
.len = 0,
|
|
||||||
.capacity = 16
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeEvent(struct Event *event)
|
|
||||||
{
|
|
||||||
free(event->methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventAdd(struct Event *event, void *instance, handler callback)
|
|
||||||
{
|
|
||||||
if (event->len == event->capacity) {
|
|
||||||
usize new_size = event->capacity * 2 * sizeof(struct Method);
|
|
||||||
struct Method *new_methods = realloc(event->methods, new_size);
|
|
||||||
|
|
||||||
if (new_methods == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
event->methods = new_methods;
|
|
||||||
event->capacity = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
event->methods[event->len++] = (struct Method) {
|
|
||||||
.callback = callback,
|
|
||||||
.instance = instance
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventInvoke(struct Event *event, void *state)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < event->len; i++) {
|
|
||||||
struct Method *method = event->methods + i;
|
|
||||||
method->callback(state, method->instance);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef void (*handler)(void *state, void *instance);
|
|
||||||
|
|
||||||
struct Method {
|
|
||||||
handler callback;
|
|
||||||
void *instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Event {
|
|
||||||
struct Method *methods;
|
|
||||||
usize len;
|
|
||||||
usize capacity;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateEvent(struct Event *event);
|
|
||||||
|
|
||||||
bool EventAdd(struct Event *event, void *instance, handler callback);
|
|
||||||
|
|
||||||
void EventInvoke(struct Event *event, void *state);
|
|
|
@ -1,69 +0,0 @@
|
||||||
#include "ringbuffer.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
void *get_ptr(RingBufferT T, struct RingBufferHead *head, usize i)
|
|
||||||
{
|
|
||||||
u8 *bytes = (u8 *)head;
|
|
||||||
return bytes + T->OFFSET + T->SIZE * i;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize RingBufferEmpty(RingBufferT T, struct RingBufferHead *head)
|
|
||||||
{
|
|
||||||
return T->LEN - head->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *RingBufferGet(RingBufferT T, struct RingBufferHead *head, usize i)
|
|
||||||
{
|
|
||||||
usize wrap_i = (head->start + i) % T->LEN;
|
|
||||||
return get_ptr(T, head, wrap_i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *RingBufferNext(RingBufferT T, struct RingBufferHead *head)
|
|
||||||
{
|
|
||||||
usize wrap_i = (head->start + head->len) % T->LEN;
|
|
||||||
return get_ptr(T, head, wrap_i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RingBufferAdd(RingBufferT T, struct RingBufferHead *dest, void *item)
|
|
||||||
{
|
|
||||||
memcpy(RingBufferNext(T, dest), item, T->SIZE);
|
|
||||||
dest->len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RingBufferTransfer(
|
|
||||||
RingBufferT T,
|
|
||||||
struct RingBufferHead *dest,
|
|
||||||
struct RingBufferHead *tmp
|
|
||||||
) {
|
|
||||||
usize copy_max = min(T->LEN - dest->len, tmp->len);
|
|
||||||
|
|
||||||
for (usize i = 0; i < copy_max; i++) {
|
|
||||||
void *to = RingBufferGet(T, dest, dest->len + i);
|
|
||||||
void *from = RingBufferGet(T, tmp, i);
|
|
||||||
memcpy(to, from, T->SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
dest->len += copy_max;
|
|
||||||
tmp->len -= copy_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize RingBufferOut(
|
|
||||||
RingBufferT T,
|
|
||||||
usize n,
|
|
||||||
void *dest,
|
|
||||||
struct RingBufferHead *src
|
|
||||||
) {
|
|
||||||
usize copy_max = min(n, src->len);
|
|
||||||
|
|
||||||
for (usize i = 0; i < copy_max; i++) {
|
|
||||||
void *to = (u8 *)dest + i * T->SIZE;
|
|
||||||
void *from = RingBufferGet(T, src, i);
|
|
||||||
memcpy(to, from, T->SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
src->len -= copy_max;
|
|
||||||
|
|
||||||
return copy_max;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdalign.h>
|
|
||||||
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
#define RINGBUF_T(RBUF_ITEM_T, RBUF_LEN) \
|
|
||||||
(&(struct RingBufferT) { \
|
|
||||||
.OFFSET = offsetof(struct { \
|
|
||||||
struct RingBufferHead head; \
|
|
||||||
RBUF_ITEM_T item; \
|
|
||||||
}, item), \
|
|
||||||
.SIZE = sizeof(RBUF_ITEM_T), \
|
|
||||||
.LEN = RBUF_LEN \
|
|
||||||
}) \
|
|
||||||
|
|
||||||
#define RINGBUF_HEAD_INIT ((struct RingBufferHead) { 0, 0 })
|
|
||||||
|
|
||||||
|
|
||||||
typedef const struct RingBufferT {
|
|
||||||
usize OFFSET;
|
|
||||||
usize SIZE;
|
|
||||||
usize LEN;
|
|
||||||
} *const RingBufferT;
|
|
||||||
|
|
||||||
struct RingBufferHead {
|
|
||||||
usize len;
|
|
||||||
usize start;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
usize RingBufferEmpty(RingBufferT T, struct RingBufferHead *head);
|
|
||||||
|
|
||||||
void *RingBufferGet(
|
|
||||||
RingBufferT T,
|
|
||||||
struct RingBufferHead *head,
|
|
||||||
usize i
|
|
||||||
);
|
|
||||||
|
|
||||||
void *RingBufferNext(RingBufferT T, struct RingBufferHead *head);
|
|
||||||
|
|
||||||
void RingBufferAdd(RingBufferT T, struct RingBufferHead *dest, void *item);
|
|
||||||
|
|
||||||
void RingBufferTransfer(
|
|
||||||
RingBufferT T,
|
|
||||||
struct RingBufferHead *dest,
|
|
||||||
struct RingBufferHead *tmp
|
|
||||||
);
|
|
||||||
|
|
||||||
usize RingBufferOut(
|
|
||||||
RingBufferT T,
|
|
||||||
usize n,
|
|
||||||
void *dest,
|
|
||||||
struct RingBufferHead *src
|
|
||||||
);
|
|
|
@ -1,47 +0,0 @@
|
||||||
#include "vector.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateVector(struct Vector *vec, usize value_size)
|
|
||||||
{
|
|
||||||
void *array = malloc(16 * value_size);
|
|
||||||
|
|
||||||
if (array == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
vec->value_size = value_size;
|
|
||||||
|
|
||||||
vec->len = 0;
|
|
||||||
vec->capacity = 16;
|
|
||||||
vec->array = array;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeVector(struct Vector *vec)
|
|
||||||
{
|
|
||||||
free(vec->array);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *VectorGet(struct Vector *vec, usize i)
|
|
||||||
{
|
|
||||||
return (u8 *)vec->array + i * vec->value_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VectorAdd(struct Vector *vec, void *item)
|
|
||||||
{
|
|
||||||
if (vec->len == vec->capacity) {
|
|
||||||
usize new_capacity = vec->capacity * 2;
|
|
||||||
void *new_array = realloc(vec->array, new_capacity * vec->value_size);
|
|
||||||
|
|
||||||
if (new_array == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
vec->capacity = new_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(VectorGet(vec, vec->len++), item, vec->value_size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Vector {
|
|
||||||
usize value_size;
|
|
||||||
|
|
||||||
usize len;
|
|
||||||
usize capacity;
|
|
||||||
void *array;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateVector(struct Vector *vec, usize value_size);
|
|
||||||
|
|
||||||
void FreeVector(struct Vector *vec);
|
|
||||||
|
|
||||||
void *VectorGet(struct Vector *vec, usize i);
|
|
||||||
|
|
||||||
bool VectorAdd(struct Vector *vec, void *item);
|
|
|
@ -1,102 +0,0 @@
|
||||||
#include "ctrl.h"
|
|
||||||
#include "ringbuffer.h"
|
|
||||||
|
|
||||||
#define INIT_SIZE 16
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateController(struct Controller *ctrl)
|
|
||||||
{
|
|
||||||
struct InputAxis *axes = calloc(16, sizeof(struct InputAxis));
|
|
||||||
if (axes == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!CreateDictionary(&ctrl->binds, sizeof(struct InputAxis*)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ctrl->pending_len = 0;
|
|
||||||
ctrl->axes = axes;
|
|
||||||
ctrl->axes_len = 0;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeController(struct Controller *ctrl)
|
|
||||||
{
|
|
||||||
free(ctrl->axes);
|
|
||||||
FreeDictionary(&ctrl->binds);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 hash_bind(u16f code, u16f type)
|
|
||||||
{
|
|
||||||
return code | (type << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ControllerBind(struct Controller *ctrl, u16 control, u16 code, u16 type)
|
|
||||||
{
|
|
||||||
u32 hash = hash_bind(code, type);
|
|
||||||
|
|
||||||
struct InputAxis *axis = &ctrl->axes[control];
|
|
||||||
struct InputAxis **bind = DictionarySet(&ctrl->binds, hash, &axis);
|
|
||||||
|
|
||||||
if (bind == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ControllerBindMulti(
|
|
||||||
struct Controller *ctrl,
|
|
||||||
usize n,
|
|
||||||
u16 *controls,
|
|
||||||
u16 *codes,
|
|
||||||
u16 *types
|
|
||||||
) {
|
|
||||||
for (usize i = 0; i < n; i++) {
|
|
||||||
if (!ControllerBind(ctrl, controls[i], codes[i], types[i]))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatch_update(struct InputAxis *axis, struct InputRecord *rec)
|
|
||||||
{
|
|
||||||
if (rec->is_down and !axis->is_held) {
|
|
||||||
axis->is_down = true;
|
|
||||||
axis->is_held = true;
|
|
||||||
axis->last_pressed = rec->time;
|
|
||||||
} else {
|
|
||||||
axis->is_up = true;
|
|
||||||
axis->is_held = false;
|
|
||||||
axis->last_released = rec->time;
|
|
||||||
}
|
|
||||||
|
|
||||||
axis->data = rec->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControllerPoll(struct Controller *ctrl, struct RecordBuffer *recs)
|
|
||||||
{
|
|
||||||
for (usize i = 0; i < ctrl->pending_len; i++) {
|
|
||||||
struct InputAxis *axis = ctrl->pending[i];
|
|
||||||
|
|
||||||
axis->is_up = false;
|
|
||||||
axis->is_down = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrl->pending_len = 0;
|
|
||||||
|
|
||||||
for (usize i = 0; i < recs->head.len; i++) {
|
|
||||||
struct InputRecord *rec = recs->buf + i;
|
|
||||||
|
|
||||||
u32 hash = hash_bind(rec->code, rec->type);
|
|
||||||
struct InputAxis **axis = DictionaryFind(&ctrl->binds, hash);
|
|
||||||
|
|
||||||
if (axis == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dispatch_update(*axis, rec);
|
|
||||||
ctrl->pending[ctrl->pending_len++] = *axis;
|
|
||||||
}
|
|
||||||
|
|
||||||
recs->head.len = 0;
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "dictionary.h"
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Controller {
|
|
||||||
struct InputAxis *pending[IO_BUF_SIZE];
|
|
||||||
usize pending_len;
|
|
||||||
|
|
||||||
struct InputAxis *axes;
|
|
||||||
usize axes_len;
|
|
||||||
|
|
||||||
struct Dictionary binds;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateController(struct Controller *ctrl);
|
|
||||||
|
|
||||||
void FreeController(struct Controller *ctrl);
|
|
||||||
|
|
||||||
bool ControllerBind(struct Controller *ctrl, u16 control, u16 code, u16 type);
|
|
||||||
|
|
||||||
bool ControllerBindMulti(
|
|
||||||
struct Controller *ctrl,
|
|
||||||
usize n,
|
|
||||||
u16 *controls,
|
|
||||||
u16 *codes,
|
|
||||||
u16 *types
|
|
||||||
);
|
|
||||||
|
|
||||||
void ControllerPoll(struct Controller *ctrl, struct RecordBuffer *recs);
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
#include "input.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include "platform.h"
|
|
||||||
|
|
||||||
|
|
||||||
RingBufferT IO_BUF_T = RINGBUF_T(struct InputRecord, IO_BUF_SIZE);
|
|
||||||
RingBufferT STR_BUF_T = RINGBUF_T(char, STR_BUF_SIZE);
|
|
||||||
|
|
||||||
|
|
||||||
void *input_worker(void *hand_arg)
|
|
||||||
{
|
|
||||||
struct InputHandle *hand = hand_arg;
|
|
||||||
|
|
||||||
struct RecordBuffer tmp_recs = { .head = RINGBUF_HEAD_INIT };
|
|
||||||
struct StringBuffer tmp_str = { .head = RINGBUF_HEAD_INIT };
|
|
||||||
|
|
||||||
while (!hand->is_terminating) {
|
|
||||||
if (!PlatformReadInput(&tmp_recs, &tmp_str)) {
|
|
||||||
hand->err = true;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pthread_mutex_lock(&hand->mutex) != 0) {
|
|
||||||
hand->err = true;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (tmp_recs.head.len == IO_BUF_SIZE) {
|
|
||||||
if (pthread_cond_wait(&hand->is_consumed, &hand->mutex) != 0) {
|
|
||||||
hand->err = true;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RingBufferTransfer(IO_BUF_T, &hand->recs.head, &tmp_recs.head);
|
|
||||||
RingBufferTransfer(STR_BUF_T, &hand->str.head, &tmp_str.head);
|
|
||||||
|
|
||||||
if (pthread_mutex_unlock(&hand->mutex) != 0) {
|
|
||||||
hand->err = true;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateInputThread(struct InputHandle *hand)
|
|
||||||
{
|
|
||||||
*hand = (struct InputHandle) {
|
|
||||||
.recs.head = RINGBUF_HEAD_INIT,
|
|
||||||
.str.head = RINGBUF_HEAD_INIT,
|
|
||||||
|
|
||||||
.err = 0,
|
|
||||||
.is_terminating = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pthread_mutex_init(&hand->mutex, nullptr) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (pthread_cond_init(&hand->is_consumed, nullptr) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (pthread_create(&hand->thread, nullptr, input_worker, hand) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JoinInputThread(struct InputHandle *hand)
|
|
||||||
{
|
|
||||||
hand->is_terminating = true;
|
|
||||||
|
|
||||||
if (!PlatformStopInput())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (pthread_mutex_destroy(&hand->mutex) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (pthread_join(hand->thread, nullptr) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InputAquire(struct InputHandle *hand)
|
|
||||||
{
|
|
||||||
if (pthread_mutex_lock(&hand->mutex) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InputRelease(struct InputHandle *hand)
|
|
||||||
{
|
|
||||||
if (pthread_cond_signal(&hand->is_consumed) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (pthread_mutex_unlock(&hand->mutex) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t InputString(struct InputHandle *hand, size_t n, char *buf)
|
|
||||||
{
|
|
||||||
return RingBufferOut(STR_BUF_T, n, buf, &hand->str.head);
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "ringbuffer.h"
|
|
||||||
|
|
||||||
#define IO_BUF_SIZE 16
|
|
||||||
#define STR_BUF_SIZE (IO_BUF_SIZE * 4)
|
|
||||||
|
|
||||||
|
|
||||||
enum InputType {
|
|
||||||
BUTTON,
|
|
||||||
AXIS,
|
|
||||||
JOYSTICK,
|
|
||||||
ESCAPE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Button {
|
|
||||||
u64 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Axis {
|
|
||||||
i64 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Joystick {
|
|
||||||
i32 x;
|
|
||||||
i32 y;
|
|
||||||
};
|
|
||||||
|
|
||||||
union InputData {
|
|
||||||
struct Button but;
|
|
||||||
struct Axis axis;
|
|
||||||
struct Joystick js;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputRecord {
|
|
||||||
nsec time;
|
|
||||||
|
|
||||||
union InputData data;
|
|
||||||
|
|
||||||
u16 code;
|
|
||||||
u16 type;
|
|
||||||
|
|
||||||
bool is_down;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputAxis {
|
|
||||||
nsec last_pressed;
|
|
||||||
nsec last_released;
|
|
||||||
|
|
||||||
union InputData data;
|
|
||||||
|
|
||||||
u16 type;
|
|
||||||
|
|
||||||
bool is_down;
|
|
||||||
bool is_held;
|
|
||||||
bool is_up;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RecordBuffer {
|
|
||||||
struct RingBufferHead head;
|
|
||||||
struct InputRecord buf[IO_BUF_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StringBuffer {
|
|
||||||
struct RingBufferHead head;
|
|
||||||
char buf[STR_BUF_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputHandle {
|
|
||||||
struct RecordBuffer recs;
|
|
||||||
struct StringBuffer str;
|
|
||||||
|
|
||||||
pthread_t thread;
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
pthread_cond_t is_consumed;
|
|
||||||
|
|
||||||
int err;
|
|
||||||
bool is_terminating;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
extern RingBufferT IO_BUF_T;
|
|
||||||
extern RingBufferT STR_BUF_T;
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateInputThread(struct InputHandle *hand);
|
|
||||||
|
|
||||||
bool JoinInputThread(struct InputHandle *hand);
|
|
||||||
|
|
||||||
bool InputAquire(struct InputHandle *hand);
|
|
||||||
|
|
||||||
bool InputRelease(struct InputHandle *hand);
|
|
||||||
|
|
||||||
size_t InputString(struct InputHandle *hand, size_t n, char *buf);
|
|
|
@ -1,48 +0,0 @@
|
||||||
#include "parseinput.h"
|
|
||||||
|
|
||||||
|
|
||||||
void ReadButton(struct InputRecord *rec, u16f code, bool is_down)
|
|
||||||
{
|
|
||||||
rec->code = code;
|
|
||||||
rec->type = BUTTON;
|
|
||||||
|
|
||||||
rec->is_down = is_down;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadAxis(struct InputRecord *rec, u16f code, u64 value)
|
|
||||||
{
|
|
||||||
rec->code = code;
|
|
||||||
rec->type = AXIS;
|
|
||||||
|
|
||||||
rec->data.axis.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadJoystick(struct InputRecord *rec, u16f code, i32 x, i32 y)
|
|
||||||
{
|
|
||||||
rec->code = code;
|
|
||||||
rec->type = JOYSTICK;
|
|
||||||
|
|
||||||
rec->data.js.x = x;
|
|
||||||
rec->data.js.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t UCS2ToUTF8(char *buf, u16f ucs2)
|
|
||||||
{
|
|
||||||
if (ucs2 < 0xFF) {
|
|
||||||
buf[0] = ucs2;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ucs2 < 0x7FF) {
|
|
||||||
buf[0] = 0xC0 + (ucs2 >> 6);
|
|
||||||
buf[1] = 0x80 + (ucs2 & 0x3F);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
buf[0] = 0xE0 + (ucs2 >> 12);
|
|
||||||
buf[1] = 0x80 + ((ucs2 >> 6) & 0x3F);
|
|
||||||
buf[2] = 0x80 + (ucs2 & 0x3F);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#include <iso646.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
|
|
||||||
void ReadButton(struct InputRecord *rec, u16f bind, bool is_down);
|
|
||||||
|
|
||||||
void ReadAxis(struct InputRecord *rec, u16f bind, u64 value);
|
|
||||||
|
|
||||||
void ReadJoystick(struct InputRecord *rec, u16f bind, i32 x, i32 y);
|
|
||||||
|
|
||||||
size_t UCS2ToUTF8(char *buf, u16f ucs2);
|
|
|
@ -1,23 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <iso646.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "win.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
bool PlatformInit();
|
|
||||||
|
|
||||||
bool PlatformGetRefreshRate(u16f *out);
|
|
||||||
|
|
||||||
bool PlatformReadInput(struct RecordBuffer *recs, struct StringBuffer *str);
|
|
||||||
|
|
||||||
bool PlatformStopInput();
|
|
||||||
|
|
||||||
bool PlatformWait(nsec duration);
|
|
|
@ -1,210 +0,0 @@
|
||||||
#include "win.h"
|
|
||||||
#include <windows.h>
|
|
||||||
#include "parseinput.h"
|
|
||||||
#include "ringbuffer.h"
|
|
||||||
|
|
||||||
#define MOUSE_MOVE (MOUSE_EVENT | MOUSE_MOVED)
|
|
||||||
#define MOUSE_VWHEEL (MOUSE_EVENT | MOUSE_WHEELED)
|
|
||||||
#define MOUSE_HWHEEL (MOUSE_EVENT | MOUSE_HWHEELED)
|
|
||||||
|
|
||||||
struct windows {
|
|
||||||
HANDLE input_hand;
|
|
||||||
HANDLE timer;
|
|
||||||
} win;
|
|
||||||
|
|
||||||
struct win_coord {
|
|
||||||
SHORT x;
|
|
||||||
SHORT y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct win_key {
|
|
||||||
BOOL is_down;
|
|
||||||
WORD repeat;
|
|
||||||
WORD vk_code;
|
|
||||||
WORD vs_code;
|
|
||||||
WCHAR ucs2_char;
|
|
||||||
DWORD state;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct win_mouse {
|
|
||||||
struct win_coord pos;
|
|
||||||
DWORD but;
|
|
||||||
DWORD state;
|
|
||||||
|
|
||||||
union {
|
|
||||||
DWORD flags;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
DWORD is_moved : 1;
|
|
||||||
DWORD is_dbl_clk : 1;
|
|
||||||
DWORD is_vwheel : 1;
|
|
||||||
DWORD is_hwheel : 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
|
||||||
DWORD : 2;
|
|
||||||
DWORD wheel : 2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct win_rec {
|
|
||||||
union {
|
|
||||||
WORD type;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
WORD is_key : 1;
|
|
||||||
WORD is_mouse : 1;
|
|
||||||
WORD is_window : 1;
|
|
||||||
WORD is_menu : 1;
|
|
||||||
WORD is_focus : 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct win_key key;
|
|
||||||
struct win_mouse mouse;
|
|
||||||
struct win_coord window;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool init_handles()
|
|
||||||
{
|
|
||||||
win.input_hand = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
if (win.input_hand == INVALID_HANDLE_VALUE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
win.timer = CreateWaitableTimerW(NULL, TRUE, NULL);
|
|
||||||
if (win.timer == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_console()
|
|
||||||
{
|
|
||||||
DWORD mode = ENABLE_EXTENDED_FLAGS
|
|
||||||
| ENABLE_PROCESSED_INPUT
|
|
||||||
| ENABLE_PROCESSED_OUTPUT
|
|
||||||
| ENABLE_MOUSE_INPUT
|
|
||||||
| ENABLE_WINDOW_INPUT;
|
|
||||||
|
|
||||||
return SetConsoleMode(win.input_hand, mode) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformInit()
|
|
||||||
{
|
|
||||||
if (!init_handles())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!init_console())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformGetRefreshRate(u16f *out)
|
|
||||||
{
|
|
||||||
DEVMODEW mode;
|
|
||||||
mode.dmSize = sizeof(DEVMODEW);
|
|
||||||
mode.dmDriverExtra = 0;
|
|
||||||
|
|
||||||
if(!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &mode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*out = mode.dmDisplayFrequency;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dispatch_rec(
|
|
||||||
struct InputRecord *rec,
|
|
||||||
struct StringBuffer *str,
|
|
||||||
struct win_rec *win
|
|
||||||
) {
|
|
||||||
u8f type = win->type | (win->is_mouse & win->mouse.flags);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case KEY_EVENT: {
|
|
||||||
ReadButton(rec, win->key.vk_code, win->key.is_down);
|
|
||||||
|
|
||||||
if (win->key.is_down)
|
|
||||||
str->head.len += UCS2ToUTF8(str->buf, win->key.ucs2_char);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case MOUSE_MOVE: {
|
|
||||||
ReadJoystick(rec, 0, win->mouse.pos.x, win->mouse.pos.y);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case MOUSE_VWHEEL: {
|
|
||||||
ReadAxis(rec, 0, win->mouse.but);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case MOUSE_HWHEEL: {
|
|
||||||
ReadAxis(rec, 1, win->mouse.but);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case WINDOW_BUFFER_SIZE_EVENT: {
|
|
||||||
// TODO: Handle window resizing
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t read_input(struct win_rec *buf, size_t n)
|
|
||||||
{
|
|
||||||
DWORD len;
|
|
||||||
if (!ReadConsoleInputW(win.input_hand, (INPUT_RECORD *)buf, n, &len))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformReadInput(struct RecordBuffer *recs, struct StringBuffer *str)
|
|
||||||
{
|
|
||||||
size_t n = RingBufferEmpty(IO_BUF_T, &recs->head);
|
|
||||||
struct win_rec buf[n];
|
|
||||||
|
|
||||||
size_t len = read_input(buf, n);
|
|
||||||
if (len == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
nsec now = TimeNow();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
struct InputRecord *rec = RingBufferNext(IO_BUF_T, &recs->head);
|
|
||||||
|
|
||||||
if (dispatch_rec(rec, str, &buf[i])) {
|
|
||||||
rec->time = now;
|
|
||||||
recs->head.len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformStopInput()
|
|
||||||
{
|
|
||||||
return CancelSynchronousIo(win.input_hand);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformWait(nsec duration)
|
|
||||||
{
|
|
||||||
LARGE_INTEGER nsec_div_100;
|
|
||||||
nsec_div_100.QuadPart = -duration / 100;
|
|
||||||
|
|
||||||
if (!SetWaitableTimer(win.timer, &nsec_div_100, 0, NULL, NULL, FALSE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DWORD result = WaitForSingleObject(win.timer, INFINITE);
|
|
||||||
if (result != WAIT_OBJECT_0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <iso646.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "fumocommon.h"
|
|
|
@ -1,141 +0,0 @@
|
||||||
#include "terminal.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define RESET_STR_LEN 7
|
|
||||||
#define MAX_CH4_LEN 11
|
|
||||||
|
|
||||||
|
|
||||||
usize TerminalMaxOut(usize wid, usize hgt)
|
|
||||||
{
|
|
||||||
return RESET_STR_LEN
|
|
||||||
+ MAX_CH4_LEN * wid * hgt
|
|
||||||
+ hgt
|
|
||||||
+ 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateTerminal(struct Terminal *term, usize wid, usize hgt)
|
|
||||||
{
|
|
||||||
struct Char4 *ch4s = calloc(wid * hgt, sizeof(struct Char4));
|
|
||||||
if (ch4s == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
char *str = malloc(TerminalMaxOut(wid, hgt));
|
|
||||||
if (str == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*term = (struct Terminal) {
|
|
||||||
.buf = ch4s,
|
|
||||||
.str = str,
|
|
||||||
|
|
||||||
.wid = wid,
|
|
||||||
.hgt = hgt
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeTerminal(struct Terminal *term)
|
|
||||||
{
|
|
||||||
free(term->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize u8_to_str(char *out, u8f x)
|
|
||||||
{
|
|
||||||
usize len = 1;
|
|
||||||
|
|
||||||
if (x > 9) {
|
|
||||||
u8f ones, tens;
|
|
||||||
|
|
||||||
ones = x % 10;
|
|
||||||
tens = x / 10;
|
|
||||||
|
|
||||||
if (x > 99) {
|
|
||||||
u8f hnds;
|
|
||||||
|
|
||||||
tens %= 10;
|
|
||||||
hnds = tens / 10;
|
|
||||||
len = 3;
|
|
||||||
|
|
||||||
out[0] = hnds + 48;
|
|
||||||
out[1] = tens + 48;
|
|
||||||
out[2] = ones + 48;
|
|
||||||
} else {
|
|
||||||
len = 2;
|
|
||||||
|
|
||||||
out[0] = tens + 48;
|
|
||||||
out[1] = ones + 48;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out[0] = x + 48;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8f ansi_bg(u8f bg)
|
|
||||||
{
|
|
||||||
return bg + (bg < 8 ? 40 : 92);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8f ansi_fg(u8f fg)
|
|
||||||
{
|
|
||||||
return fg + (fg < 8 ? 30 : 82);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize ch4_dif_to_str(char *out, struct Color4 *dif, struct Char4 *ch4)
|
|
||||||
{
|
|
||||||
usize len = 0;
|
|
||||||
|
|
||||||
if (dif->bg != ch4->color.bg) {
|
|
||||||
out[len++] = '\x1b';
|
|
||||||
out[len++] = '[';
|
|
||||||
len += u8_to_str(out + len, ansi_bg(ch4->color.bg));
|
|
||||||
|
|
||||||
if (dif->fg != ch4->color.fg) {
|
|
||||||
out[len++] = ';';
|
|
||||||
len += u8_to_str(out + len, ansi_fg(ch4->color.fg));
|
|
||||||
}
|
|
||||||
|
|
||||||
out[len++] = 'm';
|
|
||||||
|
|
||||||
} else if (dif->fg != ch4->color.fg) {
|
|
||||||
out[len++] = '\x1b';
|
|
||||||
out[len++] = '[';
|
|
||||||
len += u8_to_str(out + len, ansi_fg(ch4->color.fg));
|
|
||||||
out[len++] = 'm';
|
|
||||||
}
|
|
||||||
|
|
||||||
out[len++] = ch4->ch;
|
|
||||||
*dif = ch4->color;
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize TerminalPrint(struct Terminal *term)
|
|
||||||
{
|
|
||||||
struct Color4 dif = { 0, 0 };
|
|
||||||
|
|
||||||
usize len = 7;
|
|
||||||
memcpy(term->str, "\x1b[H\x1b[0m", 7);
|
|
||||||
|
|
||||||
struct Char4 *ch4 = term->buf;
|
|
||||||
for (usize y = 0; y < term->hgt; y++) {
|
|
||||||
for (usize x = 0; x < term->wid; x++, ch4++) {
|
|
||||||
// DEBUG
|
|
||||||
if (ch4->ch == 0)
|
|
||||||
ch4->ch = '#';
|
|
||||||
// DEBUG
|
|
||||||
|
|
||||||
len += ch4_dif_to_str(term->str + len, &dif, ch4);
|
|
||||||
}
|
|
||||||
term->str[len++] = '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
term->str[len] = 0;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Char4 *TerminalGet(struct Terminal *term, usize x, usize y)
|
|
||||||
{
|
|
||||||
return term->buf + term->wid * y + x;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "fumocommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct Color4 {
|
|
||||||
u8 bg : 4;
|
|
||||||
u8 fg : 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Char4 {
|
|
||||||
struct Color4 color;
|
|
||||||
char ch;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Terminal {
|
|
||||||
struct Char4 *buf;
|
|
||||||
char *str;
|
|
||||||
|
|
||||||
usize wid;
|
|
||||||
usize hgt;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
usize TerminalMaxOut(usize wid, usize hgt);
|
|
||||||
|
|
||||||
bool CreateTerminal(struct Terminal *term, usize wid, usize hgt);
|
|
||||||
|
|
||||||
void FreeTerminal(struct Terminal *term);
|
|
||||||
|
|
||||||
usize TerminalPrint(struct Terminal *term);
|
|
||||||
|
|
||||||
struct Char4 *TerminalGet(struct Terminal *term, usize x, usize y);
|
|
|
@ -1,177 +0,0 @@
|
||||||
#include "fumotris.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct Fumotris {
|
|
||||||
struct Terminal term;
|
|
||||||
|
|
||||||
struct TetraTemplate *bag[7];
|
|
||||||
usize bag_i;
|
|
||||||
|
|
||||||
struct Tetra board;
|
|
||||||
struct Tetra piece;
|
|
||||||
|
|
||||||
bool is_ground;
|
|
||||||
|
|
||||||
nsec last_moved;
|
|
||||||
nsec last_dropped;
|
|
||||||
};
|
|
||||||
|
|
||||||
void shuffle(struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
for (usize i = 6; i > 0; i--) {
|
|
||||||
usize swap = rand() % i;
|
|
||||||
|
|
||||||
struct TetraTemplate *tmp = fumo->bag[swap];
|
|
||||||
fumo->bag[swap] = fumo->bag[i];
|
|
||||||
fumo->bag[i] = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_clear(struct Tetra *board, usize y)
|
|
||||||
{
|
|
||||||
for (usize x = 0; x < board->wid; x++) {
|
|
||||||
if (board->blks[board->wid * y + x] == 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shift_down(struct Tetra *board, usize y)
|
|
||||||
{
|
|
||||||
memmove(board->blks + board->wid, board->blks, board->wid * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void place(struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
TetraOverlay(&fumo->piece, &fumo->board);
|
|
||||||
|
|
||||||
usize lines_cleared = 0;
|
|
||||||
for (usize y = fumo->piece.y; y < fumo->piece.y + fumo->piece.hgt; y++) {
|
|
||||||
if (check_clear(&fumo->board, y)) {
|
|
||||||
lines_cleared += 1;
|
|
||||||
shift_down(&fumo->board, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TetraSet(&fumo->piece, fumo->bag[fumo->bag_i++]);
|
|
||||||
|
|
||||||
if (fumo->bag_i == 7) {
|
|
||||||
shuffle(fumo);
|
|
||||||
fumo->bag_i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i16 get_horizontal(struct Controller *ctrl)
|
|
||||||
{
|
|
||||||
return (-(i16)ctrl->axes[LEFT].is_held) + ctrl->axes[RIGHT].is_held;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FumotrisStart(struct Instance *inst, struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
ControllerBindMulti(&inst->ctrl, BINDS_N, CONTROLS, CODES, TYPES);
|
|
||||||
|
|
||||||
CreateTerminal(&fumo->term, 64, 26);
|
|
||||||
FILE *file;
|
|
||||||
file = fopen("fumo.txt", "r");
|
|
||||||
if (file) {
|
|
||||||
signed char ch;
|
|
||||||
usize x = 0;
|
|
||||||
usize y = 0;
|
|
||||||
|
|
||||||
while ((ch = getc(file)) != EOF) {
|
|
||||||
if (ch == '\n') {
|
|
||||||
x = 0;
|
|
||||||
y += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fumo->term.buf[fumo->term.wid * y + x++] = (struct Char4) { .ch = ch, .color.fg = 15 };
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (usize i = 0; i < 7; i++)
|
|
||||||
fumo->bag[i] = templates[i];
|
|
||||||
fumo->bag_i = 1;
|
|
||||||
|
|
||||||
CreateTetra(&fumo->board, 10, 20);
|
|
||||||
TetraSet(&fumo->piece, fumo->bag[0]);
|
|
||||||
|
|
||||||
fumo->is_ground = false;
|
|
||||||
fumo->last_moved = 0;
|
|
||||||
fumo->last_dropped = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FumotrisUpdate(struct Instance *inst, struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
i16 horizontal = get_horizontal(&inst->ctrl);
|
|
||||||
|
|
||||||
if (horizontal != 0 and fumo->last_moved < inst->time) {
|
|
||||||
fumo->last_moved = inst->time + 1e8;
|
|
||||||
TetraMove(&fumo->piece, &fumo->board, horizontal, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst->ctrl.axes[ROTATE_CCW].is_down)
|
|
||||||
TetraRotate(&fumo->piece, &fumo->board, 1);
|
|
||||||
|
|
||||||
if (inst->ctrl.axes[ROTATE_CW].is_down)
|
|
||||||
TetraRotate(&fumo->piece, &fumo->board, -1);
|
|
||||||
|
|
||||||
if (inst->ctrl.axes[SOFT_DROP].is_held and fumo->last_dropped < inst->time) {
|
|
||||||
fumo->last_dropped = inst->time + 1e8;
|
|
||||||
TetraMove(&fumo->piece, &fumo->board, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst->ctrl.axes[HARD_DROP].is_down) {
|
|
||||||
while (TetraMove(&fumo->piece, &fumo->board, 0, 1));
|
|
||||||
place(fumo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsec FumotrisFall(struct Instance *inst, struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
if (!TetraMove(&fumo->piece, &fumo->board, 0, 1)) {
|
|
||||||
if (!fumo->is_ground)
|
|
||||||
fumo->is_ground = true;
|
|
||||||
else
|
|
||||||
place(fumo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 5e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FumotrisDraw(struct Instance *inst, struct Fumotris *fumo)
|
|
||||||
{
|
|
||||||
TetraTerminalClear(&fumo->board, &fumo->term, 3, 5);
|
|
||||||
TetraTerminalDraw(&fumo->board, &fumo->term, 3, 5);
|
|
||||||
|
|
||||||
TetraTerminalDrawGhost(&fumo->piece, &fumo->board, &fumo->term, 3, 5);
|
|
||||||
TetraTerminalDraw(&fumo->piece, &fumo->term, 3, 5);
|
|
||||||
|
|
||||||
TerminalPrint(&fumo->term);
|
|
||||||
puts(fumo->term.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
system("color");
|
|
||||||
|
|
||||||
struct Instance inst;
|
|
||||||
CreateFumoInstance(&inst);
|
|
||||||
|
|
||||||
struct Fumotris game;
|
|
||||||
|
|
||||||
EventAdd(&inst.on_start, &game, FumotrisStart);
|
|
||||||
EventAdd(&inst.on_update, &game, FumotrisUpdate);
|
|
||||||
EventAdd(&inst.on_draw, &game, FumotrisDraw);
|
|
||||||
|
|
||||||
CoroutineAdd(&inst, &game, FumotrisFall);
|
|
||||||
|
|
||||||
FumoInstanceRun(&inst);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "fumoengine.h"
|
|
||||||
#include "terminal.h"
|
|
||||||
#include "tetra.h"
|
|
||||||
|
|
||||||
#define BINDS_N 12
|
|
||||||
|
|
||||||
|
|
||||||
enum FumotrisControls {
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
SOFT_DROP,
|
|
||||||
HARD_DROP,
|
|
||||||
ROTATE_CCW,
|
|
||||||
ROTATE_CW,
|
|
||||||
ROTATE_180,
|
|
||||||
SWAP,
|
|
||||||
ESC,
|
|
||||||
|
|
||||||
VSCROLL,
|
|
||||||
HSCROLL,
|
|
||||||
|
|
||||||
MOUSE
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
u16 CONTROLS[BINDS_N] = {
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
SOFT_DROP,
|
|
||||||
HARD_DROP,
|
|
||||||
ROTATE_CCW,
|
|
||||||
ROTATE_CW,
|
|
||||||
ROTATE_180,
|
|
||||||
SWAP,
|
|
||||||
ESC,
|
|
||||||
|
|
||||||
VSCROLL,
|
|
||||||
HSCROLL,
|
|
||||||
|
|
||||||
MOUSE,
|
|
||||||
};
|
|
||||||
|
|
||||||
u16 CODES[BINDS_N] = {
|
|
||||||
0x25,
|
|
||||||
0x27,
|
|
||||||
0x28,
|
|
||||||
0x20,
|
|
||||||
'Z',
|
|
||||||
'X',
|
|
||||||
'A',
|
|
||||||
'C',
|
|
||||||
0x1B,
|
|
||||||
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
u16 TYPES[BINDS_N] = {
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
BUTTON,
|
|
||||||
|
|
||||||
AXIS,
|
|
||||||
AXIS,
|
|
||||||
|
|
||||||
JOYSTICK
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate I = {
|
|
||||||
.blks = (u8[16]) {
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 4,
|
|
||||||
.hgt = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate O = {
|
|
||||||
.blks = (u8[4]) {
|
|
||||||
2, 2,
|
|
||||||
2, 2
|
|
||||||
},
|
|
||||||
.wid = 2,
|
|
||||||
.hgt = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate T = {
|
|
||||||
.blks = (u8[9]) {
|
|
||||||
0, 3, 0,
|
|
||||||
3, 3, 3,
|
|
||||||
0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 3,
|
|
||||||
.hgt = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate S = {
|
|
||||||
.blks = (u8[9]) {
|
|
||||||
0, 4, 4,
|
|
||||||
4, 4, 0,
|
|
||||||
0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 3,
|
|
||||||
.hgt = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate Z = {
|
|
||||||
.blks = (u8[9]) {
|
|
||||||
5, 5, 0,
|
|
||||||
0, 5, 5,
|
|
||||||
0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 3,
|
|
||||||
.hgt = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate J = {
|
|
||||||
.blks = (u8[9]) {
|
|
||||||
6, 0, 0,
|
|
||||||
6, 6, 6,
|
|
||||||
0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 3,
|
|
||||||
.hgt = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate L = {
|
|
||||||
.blks = (u8[9]) {
|
|
||||||
0, 0, 7,
|
|
||||||
7, 7, 7,
|
|
||||||
0, 0, 0
|
|
||||||
},
|
|
||||||
.wid = 3,
|
|
||||||
.hgt = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TetraTemplate *templates[7] = { &I, &O, &T, &S, &Z, &J, &L };
|
|
|
@ -1,183 +0,0 @@
|
||||||
#include "tetra.h"
|
|
||||||
|
|
||||||
|
|
||||||
bool CreateTetra(struct Tetra *tetra, u16 wid, u16 hgt)
|
|
||||||
{
|
|
||||||
u8 *blks = calloc(wid * hgt, sizeof(u8));
|
|
||||||
|
|
||||||
if (blks == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*tetra = (struct Tetra) {
|
|
||||||
.blks = blks,
|
|
||||||
|
|
||||||
.wid = wid,
|
|
||||||
.hgt = hgt,
|
|
||||||
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
|
|
||||||
.rot = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeTetra(struct Tetra *tetra)
|
|
||||||
{
|
|
||||||
free(tetra->blks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TetraSet(struct Tetra *tetra, struct TetraTemplate *template)
|
|
||||||
{
|
|
||||||
tetra->blks = template->blks;
|
|
||||||
tetra->wid = template->wid;
|
|
||||||
tetra->hgt = template->hgt;
|
|
||||||
|
|
||||||
tetra->x = 0;
|
|
||||||
tetra->y = 0;
|
|
||||||
tetra->rot = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
usize rotate_index(usize i, usize wid, u8 rot)
|
|
||||||
{
|
|
||||||
if(rot == 0)
|
|
||||||
return i;
|
|
||||||
|
|
||||||
usize row = i / wid;
|
|
||||||
usize col = i % wid;
|
|
||||||
|
|
||||||
switch (rot) {
|
|
||||||
case 1:
|
|
||||||
return (wid - col - 1) * wid + row;
|
|
||||||
case 2:
|
|
||||||
return (wid - row - 1) * wid + (wid - col - 1);
|
|
||||||
case 3:
|
|
||||||
return col * wid + (wid - row - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TetraIsCollision(struct Tetra *piece, struct Tetra *board)
|
|
||||||
{
|
|
||||||
usize i = 0;
|
|
||||||
for (i16 y = piece->y; y < piece->y + piece->hgt; y++) {
|
|
||||||
for (i16 x = piece->x; x < piece->x + piece->wid; x++, i++) {
|
|
||||||
usize rot_i = rotate_index(i, piece->wid, piece->rot);
|
|
||||||
|
|
||||||
if(piece->blks[rot_i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(x < 0 or x >= board->wid or y < 0 or y >= board->hgt)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if(board->blks[board->wid * y + x] != 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TetraMove(struct Tetra *t, struct Tetra *board, i16 dx, i16 dy)
|
|
||||||
{
|
|
||||||
t->x += dx;
|
|
||||||
t->y += dy;
|
|
||||||
|
|
||||||
if (TetraIsCollision(t, board)) {
|
|
||||||
t->x -= dx;
|
|
||||||
t->y -= dy;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TetraRotate(struct Tetra *t, struct Tetra *board, i8 dr)
|
|
||||||
{
|
|
||||||
u8 rot = t->rot;
|
|
||||||
t->rot = (t->rot + 4 + dr) % 4;
|
|
||||||
|
|
||||||
if (TetraIsCollision(t, board)) {
|
|
||||||
t->rot = rot;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TetraOverlay(struct Tetra *t, struct Tetra *board)
|
|
||||||
{
|
|
||||||
usize i = 0;
|
|
||||||
for (i16 y = t->y; y < t->y + t->hgt; y++) {
|
|
||||||
for (i16 x = t->x; x < t->x + t->wid; x++, i++) {
|
|
||||||
usize rot_i = rotate_index(i, t->wid, t->rot);
|
|
||||||
|
|
||||||
if(t->blks[rot_i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(x < 0 or x >= board->wid or y < 0 or y >= board->hgt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
board->blks[board->wid * y + x] = t->blks[rot_i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TetraTerminalClear(struct Tetra *board, struct Terminal *term, u16 OFS_X, u16 OFS_Y)
|
|
||||||
{
|
|
||||||
for (i16 y = board->y; y < board->y + board->hgt; y++) {
|
|
||||||
for (i16 x = board->x; x < board->x + board->wid; x++) {
|
|
||||||
struct Char4 *ch4 = TerminalGet(term, (x * 2) + OFS_X, y + OFS_Y);
|
|
||||||
|
|
||||||
ch4[0] = (struct Char4) { .ch = '.', .color.fg = 8 };
|
|
||||||
ch4[1] = (struct Char4) { .ch = ' ', .color.fg = 8 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TetraTerminalDraw(struct Tetra *piece, struct Terminal *term, u16 OFS_X, u16 OFS_Y)
|
|
||||||
{
|
|
||||||
static const u8f blk_colors[8] = { 8, 14, 11, 13, 10, 9, 12, 3 };
|
|
||||||
|
|
||||||
usize i = 0;
|
|
||||||
for (i16 y = piece->y; y < piece->y + piece->hgt; y++) {
|
|
||||||
for (i16 x = piece->x; x < piece->x + piece->wid; x++, i++) {
|
|
||||||
usize rot_i = rotate_index(i, piece->wid, piece->rot);
|
|
||||||
|
|
||||||
if (piece->blks[rot_i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
struct Char4 *ch4 = TerminalGet(term, (x * 2) + OFS_X, y + OFS_Y);
|
|
||||||
|
|
||||||
u8 fg = blk_colors[piece->blks[rot_i]];
|
|
||||||
|
|
||||||
ch4[0] = (struct Char4) { .ch = '[', .color.fg = fg };
|
|
||||||
ch4[1] = (struct Char4) { .ch = ']', .color.fg = fg };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TetraTerminalDrawGhost(struct Tetra *t, struct Tetra *board, struct Terminal *term, u16 OFS_X, u16 OFS_Y)
|
|
||||||
{
|
|
||||||
struct Tetra ghost = *t;
|
|
||||||
while(TetraMove(&ghost, board, 0, 1));
|
|
||||||
|
|
||||||
usize i = 0;
|
|
||||||
for (i16 y = ghost.y; y < ghost.y + ghost.hgt; y++) {
|
|
||||||
for (i16 x = ghost.x; x < ghost.x + ghost.wid; x++, i++) {
|
|
||||||
usize rot_i = rotate_index(i, ghost.wid, ghost.rot);
|
|
||||||
|
|
||||||
if (ghost.blks[rot_i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
struct Char4 *ch4 = TerminalGet(term, (x * 2) + OFS_X, y + OFS_Y);
|
|
||||||
|
|
||||||
ch4[0] = (struct Char4) { .ch = '[', .color.fg = 8 };
|
|
||||||
ch4[1] = (struct Char4) { .ch = ']', .color.fg = 8 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "fumocommon.h"
|
|
||||||
#include "terminal.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct TetraTemplate {
|
|
||||||
u8 *blks;
|
|
||||||
|
|
||||||
usize wid;
|
|
||||||
usize hgt;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Tetra {
|
|
||||||
u8 *blks;
|
|
||||||
|
|
||||||
u16 wid;
|
|
||||||
u16 hgt;
|
|
||||||
|
|
||||||
i16 x;
|
|
||||||
i16 y;
|
|
||||||
u8 rot;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CreateTetra(struct Tetra *tetra, u16 wid, u16 hgt);
|
|
||||||
|
|
||||||
void FreeTetra(struct Tetra *tetra);
|
|
||||||
|
|
||||||
void TetraSet(struct Tetra *tetra, struct TetraTemplate *template);
|
|
||||||
|
|
||||||
bool TetraIsCollision(struct Tetra *t, struct Tetra *board);
|
|
||||||
|
|
||||||
bool TetraMove(struct Tetra *t, struct Tetra *board, i16 dx, i16 dy);
|
|
||||||
|
|
||||||
bool TetraRotate(struct Tetra *t, struct Tetra *board, i8 dr);
|
|
||||||
|
|
||||||
void TetraOverlay(struct Tetra *t, struct Tetra *board);
|
|
||||||
|
|
||||||
void TetraTerminalClear(struct Tetra *board, struct Terminal *term, u16 OFS_X, u16 OFS_Y);
|
|
||||||
|
|
||||||
void TetraTerminalDraw(struct Tetra *piece, struct Terminal *term, u16 OFS_X, u16 OFS_Y);
|
|
||||||
|
|
||||||
void TetraTerminalDrawGhost(struct Tetra *t, struct Tetra *board, struct Terminal *term, u16 OFS_X, u16 OFS_Y);
|
|
Loading…
Reference in a new issue