File organization, input system work

Still working on the rewrite's input system
This commit is contained in:
Julia 2024-06-09 15:58:28 -05:00
parent 0c2653ffaa
commit d776cf6b46
59 changed files with 124 additions and 2621 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
#FOLDERS
source/
old source/
.vscode/

BIN
a.exe

Binary file not shown.

View file

@ -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.

Binary file not shown.

View file

@ -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;
}

BIN
meow.exe

Binary file not shown.

View file

@ -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;
}

View file

@ -1,8 +0,0 @@
#include "fumocommon.h"
struct Pool {
void *memory;
usize size;
usize allocated;
};

View file

@ -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++) {
}
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -1,10 +0,0 @@
#pragma once
#include "fumocommon.h"
struct DictionaryTemplate {
u32 *keys;
void **value_ptrs;
usize n;
};

View file

@ -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;
}

View file

@ -1,9 +0,0 @@
#include "input.h"
void *input_worker(void *hand_arg)
{
while(true) {
PlatformReadInput();
}
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}
}
};

View file

@ -82,16 +82,16 @@ pub fn HashMap(comptime V: type) type {
pub fn Buffer(comptime T: type, comptime size: comptime_int) type {
return struct {
array: [size]T = undefined,
len: usize = 0,
read: usize = 0,
pub fn slice(this: *@This()) []T {
return this.array[this.read..this.len];
}
written: usize = 0,
used: usize = 0,
pub fn flush(this: *@This()) void {
this.len = 0;
this.read = 0;
this.written = 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,
write: usize = 0,
cond: std.Thread.Condition = .{},
mutex: std.Thread.Mutex = .{},
pub fn write(this: *@This(), slice: []T) usize {
const read_atm: usize = @atomicLoad(usize, &this.read, AtomicOrder.acquire);
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);
return struct { region_a, region_b };
}
pub fn waitRead(this: *@This()) void {
this.mutex.lock();
this.cond.wait(&this.mutex);
this.mutex.unlock();
}
};
}

View file

@ -3,7 +3,6 @@ const fumo = @import("fumostd.zig");
const platform = @import("platform.zig");
pub const RecordCircle = fumo.CircleBuffer(Record, 16);
pub const StringCircle = fumo.CircleBuffer(u8, 16 * 4);
pub const RecordBuffer = fumo.Buffer(Record, 16);
pub const StringBuffer = fumo.Buffer(u8, 16 * 4);
@ -12,20 +11,20 @@ pub const Record = struct {
const Type = enum { axis, joystick };
hash: usize,
data: f64,
value: f64,
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),
.data = data,
};
buf.len += 1;
buf.written += 1;
}
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.len + 1] = .{ .code = scancode | Record.joystick_ofs, .data = y };
buf.len += 2;
buf.array[buf.written] = .{ .code = hashCode(code, Type.joystick), .data = x };
buf.array[buf.written + 1] = .{ .code = hashCode(code, Type.joystick), .data = y };
buf.written += 2;
}
fn hashCode(code: usize, code_type: usize) usize {
@ -35,10 +34,10 @@ pub const Record = struct {
pub const Handle = struct {
rec_circ: RecordCircle = .{},
str_circ: StringCircle = .{},
thread: std.Thread = undefined,
signal: std.Thread.Condition = .{},
cond: std.Thread.Condition = .{},
mutex: std.Thread.Mutex = .{},
pub fn init() !Handle {
var handle = Handle{};
@ -48,21 +47,98 @@ pub const Handle = struct {
}
fn worker(hand: *Handle) !void {
var rec_buf = RecordBuffer{};
var str_buf = StringBuffer{};
var new_recs = RecordBuffer{};
while (true) : ({
rec_buf.flush();
str_buf.flush();
}) {
try platform.ReadInput(&rec_buf, &str_buf);
while (true) : (new_recs.flush()) {
try platform.ReadInput(&new_recs, &str_buf);
while (rec_buf.read < rec_buf.len) : ({
hand.signal.wait(&hand.mutex);
}) {
rec_buf.read += hand.rec_circ.write(rec_buf.slice());
str_buf.read += hand.str_circ.write(str_buf.slice());
while (new_recs.used < new_recs.written) : (rec_circ.waitRead()) {
new_recs.used += hand.rec_circ.write(new_recs.getSlice());
}
}
}
};
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;
}
}
};

View file

@ -1,25 +1,20 @@
const std = @import("std");
const fumo = @import("fumostd.zig");
const controller = @import("controller.zig");
const input = @import("input.zig");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_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);
ctrl.bindAxis(0, 2, 1);
try ctrl.mapCodeToBind(69, 0);
ctrl.mapBindToAxis(0, 2, 1);
var hand = try input.Handle.init();
hand.aquire();
_ = hand.rec_circ.write(input.Record.readAxis(69, 123));
ctrl.poll(&hand.rec_circ);
std.debug.print("axis index 2: {}", .{ctrl.axes[2].data});
hand.release();
while (true) {
ctrl.poll(&hand.rec_circ);
hand.cond.signal();
}
}

View file

@ -47,8 +47,8 @@ fn translate(
if (!win_rec.Event.KeyEvent.bKeyDown)
return;
str_buf.len += sprintWchar(
str_buf.array + str_buf.len,
str_buf.written += sprintWchar(
str_buf.array + str_buf.written,
win_rec.Event.KeyEvent.uChar.UnicodeChar,
);
},

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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
);

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -1,7 +0,0 @@
#pragma once
#include <iso646.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "fumocommon.h"

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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 };

View file

@ -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 };
}
}
}

View file

@ -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);

BIN
test.exe

Binary file not shown.

BIN
whar.exe

Binary file not shown.