a
This commit is contained in:
Julia 2024-06-08 15:20:47 -05:00
parent 6477a45b8b
commit 0c2653ffaa
19 changed files with 229 additions and 139 deletions

BIN
music/3xosc.flp Normal file

Binary file not shown.

BIN
music/chips/chipbgm.mp3 Normal file

Binary file not shown.

BIN
music/chips/chips.flp Normal file

Binary file not shown.

View file

@ -3,14 +3,14 @@ const fumo = @import("fumostd.zig");
const input = @import("input.zig"); const input = @import("input.zig");
const Axis = struct { const Axis = struct {
data: f64, data: f64 = 0,
last_pressed: u64, last_pressed: u64 = 0,
last_released: u64, last_released: u64 = 0,
is_down: bool, is_down: bool = false,
is_held: bool, is_held: bool = false,
is_up: bool, is_up: bool = false,
}; };
const Bind = struct { const Bind = struct {
@ -18,37 +18,37 @@ const Bind = struct {
multiplier: f64, multiplier: f64,
}; };
const Controller = struct { pub const Controller = struct {
const BindHashMap = fumo.HashMap(*Bind); const BindHashMap = fumo.HashMap(*Bind);
axes: []Axis, axes: []Axis,
binds: []Bind, binds: []Bind,
scancode_map: BindHashMap, code_map: BindHashMap,
pending: [input.IO_BUF_SIZE]*Axis = undefined, pending: [16]*Axis = undefined,
n_pending: usize = 0, n_pending: usize = 0,
pub fn init( pub fn init(
arena: std.heap.ArenaAllocator, arena: std.mem.Allocator,
n_axes: usize, n_axes: usize,
n_binds: usize, n_binds: usize,
n_scancodes: usize, n_codes: usize,
) !@This() { ) !@This() {
const ctrl = @This(){ const ctrl = @This(){
.axes = try arena.alloc(Axis, n_axes), .axes = try arena.alloc(Axis, n_axes),
.binds = try arena.alloc(Bind, n_binds), .binds = try arena.alloc(Bind, n_binds),
.scancode_map = try BindHashMap.init(arena, n_scancodes), .code_map = try BindHashMap.init(arena, n_codes),
}; };
@memset(ctrl.axes, 0); @memset(ctrl.axes, Axis{});
return ctrl; return ctrl;
} }
pub fn bindScancode(this: *@This(), scancode: usize, bind: usize) !void { pub fn bindCode(this: *@This(), code: usize, bind: usize) !void {
try this.scancode_map.set(scancode, &this.binds[bind]); try this.code_map.set(code, &this.binds[bind]);
} }
pub fn bindAxis(this: *@This(), bind: usize, axis: usize, mul: f64) void { pub fn bindAxis(this: *@This(), bind: usize, axis: usize, mul: f64) void {
@ -58,29 +58,28 @@ const Controller = struct {
}; };
} }
pub fn poll(this: *@This(), record_buffer: fumo.RingBuffer(input.Record, 16)) void { pub fn poll(this: *@This(), rec_circ: *input.RecordCircle) void {
for (this.pending[0..this.n_pending]) |axis| { for (this.pending[0..this.n_pending]) |axis| {
axis.is_up = false; axis.is_up = false;
axis.is_down = false; axis.is_down = false;
} }
for (record_buffer) |*record| { inline for (rec_circ.read()) |slice| for (slice) |record| {
if (this.scancode_map.find(record.scancode)) |*bind| { if (this.code_map.find(record.code)) |bind| {
dispatch(bind, record); dispatch(bind, record);
} }
} };
} }
fn dispatch(bind: *Bind, record: input.Record) void { fn dispatch(bind: *Bind, record: input.Record) void {
bind.axis.data = record.data * bind.multiplier; bind.axis.data = record.data * bind.multiplier;
if (record.is_down) { bind.axis.is_down = record.data != 0;
bind.axis.is_down = true; bind.axis.is_up = record.data == 0;
bind.axis.is_held = true;
if (record.data != 0) {
bind.axis.last_pressed = record.time; bind.axis.last_pressed = record.time;
} else { } else {
bind.axis.is_up = true;
bind.axis.is_held = false;
bind.axis.last_released = record.time; bind.axis.last_released = record.time;
} }
} }

View file

@ -1,12 +1,13 @@
const std = @import("std"); const std = @import("std");
const AtomicOrder = @import("std").builtin.AtomicOrder;
pub fn HashMap(comptime V: type) type { pub fn HashMap(comptime V: type) type {
return struct { return struct {
pub const HashMapError = error{Full}; pub const HashMapError = error{Full};
pub const Bucket = struct { pub const Bucket = struct {
hash: usize, hash: usize = 0,
value: V, value: V = undefined,
}; };
buckets: []Bucket, buckets: []Bucket,
@ -17,7 +18,7 @@ pub fn HashMap(comptime V: type) type {
.buckets = try allocator.alloc(Bucket, n), .buckets = try allocator.alloc(Bucket, n),
}; };
@memset(map.buckets, Bucket{ .hash = 0, .value = undefined }); @memset(map.buckets, Bucket{});
return map; return map;
} }
@ -78,46 +79,63 @@ pub fn HashMap(comptime V: type) type {
}; };
} }
pub fn RingBuffer(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,
read_index: isize = 0, len: usize = 0,
write_index: isize = 0, read: usize = 0,
pub fn write(this: *@This(), slice: []T) usize { pub fn slice(this: *@This()) []T {
const write_limit = @mod(this.read_index - 1, size); return this.array[this.read..this.len];
var i: usize = 0;
while (i < slice.len and this.write_index != write_limit) : ({
i += 1;
}) {
this.array[this.write_index] = slice[i];
this.write_index = @mod(this.write_index + 1, size);
}
return i;
} }
pub fn read(this: *@This()) struct { []T, []T } { pub fn flush(this: *@This()) void {
const contiguous: bool = this.write_index >= this.read_index; this.len = 0;
this.read = 0;
const upper_1: usize = if (contiguous) this.write_index else size; }
const upper_2: usize = if (contiguous) 0 else this.write_index; };
}
this.read_index = this.write_index;
pub fn CircleBuffer(comptime T: type, comptime size: comptime_int) type {
return .{ return struct {
this.array[this.read_index..upper_1], array: [size]T = undefined,
this.array[0..upper_2], read: usize = 0,
}; write: usize = 0,
}
pub fn write(this: *@This(), slice: []T) usize {
pub fn unreadLen(this: @This()) usize { const read_atm: usize = @atomicLoad(usize, &this.read, AtomicOrder.acquire);
if (this.write_index >= this.read_index) { const contiguous: bool = this.write < read_atm;
return this.write_index - this.read_index;
} else { const a_upper: usize = if (contiguous) read_atm else size;
return (size - this.read_index) + this.write_index; const b_upper: usize = if (contiguous) 0 else read_atm;
}
const region_a: []T = this.array[this.write..a_upper];
const region_b: []T = this.array[0..b_upper];
const slice_a: []T = slice[0..@min(slice.len, region_a.len)];
const slice_b: []T = slice[slice_a.len..slice.len];
@memcpy(region_a, slice_a);
@memcpy(region_b, slice_b);
const written: usize = @min(slice.len, region_a.len + region_b.len);
@atomicStore(usize, &this.write, (this.write + written) % size, AtomicOrder.release);
return written;
}
pub fn read(this: *@This()) struct { []T, []T } {
const write_atm: usize = @atomicLoad(usize, &this.write, AtomicOrder.acquire);
const contiguous: bool = this.read < write_atm;
const a_upper: usize = if (contiguous) write_atm else size;
const b_upper: usize = if (contiguous) 0 else write_atm;
const region_a: []T = this.array[this.read..a_upper];
const region_b: []T = this.array[0..b_upper];
const read_total: usize = region_a.len + region_b.len;
@atomicStore(usize, &this.read, (this.read + read_total) % size, AtomicOrder.release);
return struct { region_a, region_b };
} }
}; };
} }

View file

@ -1,61 +1,67 @@
const std = @import("std"); const std = @import("std");
const fumo = @import("fumostd.zig"); const fumo = @import("fumostd.zig");
const platform = @import("platform.zig");
const IO_BUF_SIZE: usize = 16; pub const RecordCircle = fumo.CircleBuffer(Record, 16);
const STR_BUF_SIZE: usize = IO_BUF_SIZE * 4; pub const StringCircle = fumo.CircleBuffer(u8, 16 * 4);
const RecordBuffer = fumo.RingBuffer(Record, IO_BUF_SIZE); pub const RecordBuffer = fumo.Buffer(Record, 16);
const StringBuffer = fumo.RingBuffer(u8, STR_BUF_SIZE); pub const StringBuffer = fumo.Buffer(u8, 16 * 4);
const Record = struct { pub const Record = struct {
scancode: usize, const Type = enum { axis, joystick };
hash: usize,
data: f64, data: f64,
time: u64, pub fn readAxis(buf: RecordBuffer, code: usize, data: f64) void {
buf.array[buf.len] = Record{
.hash = hashCode(code, Type.axis),
.data = data,
};
buf.len += 1;
}
is_down: bool, 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;
}
fn hashCode(code: usize, code_type: usize) usize {
return code | (code_type << (@bitSizeOf(usize) / 2));
}
}; };
const Handle = struct { pub const Handle = struct {
records: RecordBuffer, rec_circ: RecordCircle = .{},
string: StringBuffer, str_circ: StringCircle = .{},
thread: std.Thread, thread: std.Thread = undefined,
mutex: std.Thread.Mutex = std.Thread.Mutex{}, signal: std.Thread.Condition = .{},
read_signal: std.Thread.Condition = std.Thread.Condition{},
pub fn init() !Handle { pub fn init() !Handle {
const handle = Handle{ var handle = Handle{};
.thread = try std.Thread.spawn(.{}, worker, .{}), handle.thread = try std.Thread.spawn(.{}, worker, .{&handle});
};
return handle; return handle;
} }
fn worker(this: *@This()) void { fn worker(hand: *Handle) !void {
var rec_buf: [IO_BUF_SIZE]Record = undefined; var rec_buf = RecordBuffer{};
var str_buf: [STR_BUF_SIZE]u8 = undefined; var str_buf = StringBuffer{};
var n_recs: usize = 0; while (true) : ({
var n_str: usize = 0; rec_buf.flush();
str_buf.flush();
}) {
try platform.ReadInput(&rec_buf, &str_buf);
while (true) { while (rec_buf.read < rec_buf.len) : ({
// block platform read in hand.signal.wait(&hand.mutex);
try platform.ReadInput(&rec_buf, &n_recs, &str_buf, &n_str);
this.mutex.lock();
defer this.mutex.unlock();
var w_recs: usize = 0;
var w_str: usize = 0;
while (w_recs < n_recs) : ({
this.read_signal.wait(&this.mutex);
if (this.records.read_index == this.records.write_index)
continue;
}) { }) {
w_recs += this.records.write(rec_buf[w_recs..n_recs]); rec_buf.read += hand.rec_circ.write(rec_buf.slice());
w_str += this.string.write(str_buf[w_str..n_str]); str_buf.read += hand.str_circ.write(str_buf.slice());
} }
} }
} }

View file

@ -1,11 +1,25 @@
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");
pub fn main() !void { pub fn main() !void {
const a: isize = 0; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const b: isize = 6; const allocator = arena.allocator();
std.debug.print("{}", .{@mod(a - 1, b)}); var ctrl = try controller.Controller.init(allocator, 3, 3, 3);
try ctrl.bindCode(69, 0);
ctrl.bindAxis(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();
} }
test "dict" {}

View file

@ -10,47 +10,100 @@ const stdin = windows.GetStdHandle(windows.STD_INPUT_HANDLE);
const PlatformError = error{ReadInputCall}; const PlatformError = error{ReadInputCall};
pub fn ReadInput( pub fn ReadInput(
out_buf: *[input.IO_BUF_SIZE]input.Record, rec_buf: *input.RecordBuffer,
n_out: *usize, str_buf: *input.StringBuffer,
str_buf: *[input.STR_BUF_SIZE]u8,
n_str: *usize,
) !void { ) !void {
var buf: [input.IO_BUF_SIZE]windows.INPUT_RECORD = undefined; var win_buf: [input.IO_BUF_SIZE]windows.INPUT_RECORD = undefined;
var n_buf: usize = 0; var n_win_buf: usize = 0;
const success: bool = windows.ReadConsoleInputW( const success: bool = windows.ReadConsoleInputW(
stdin, stdin,
&buf, &win_buf,
input.IO_BUF_SIZE, input.IO_BUF_SIZE,
&n_buf, &n_win_buf,
); );
if (!success) if (!success)
return PlatformError.ReadInputCall; return PlatformError.ReadInputCall;
n_out.* = 0; for (win_buf) |win_rec| {
translate(win_rec, rec_buf, str_buf);
for (buf, out_buf) |rec, *out_rec| {}
}
fn dispatch(win_rec: windows.INPUT_RECORD) bool {
switch (win_rec.EventType) {
windows.KEY_EVENT => blk: {
out_rec.* = input.Record{
.scancode = rec.Event.KeyEvent.wVirtualScanCode,
.data = rec.Event.KeyEvent.bKeyDown,
.time = now,
.is_down = rec.Event.KeyEvent.bKeyDown,
};
if (rec.Event.KeyEvent.bKeyDown)
n_str.* += ucs2ToUtf8(str_buf + n_out, rec.Event.KeyEvent.uChar.UnicodeChar);
break :blk true;
},
windows.WINDOW_BUFFER_SIZE_EVENT => false,
else => false,
} }
} }
fn translate(
win_rec: windows.INPUT_RECORD,
rec_buf: *input.RecordBuffer,
str_buf: *input.StringBuffer,
) void {
switch (win_rec.EventType) {
windows.KEY_EVENT => {
input.Record.readAxis(
rec_buf,
win_rec.Event.KeyEvent.wVirtualKeyCode,
win_rec.Event.KeyEvent.bKeyDown,
);
if (!win_rec.Event.KeyEvent.bKeyDown)
return;
str_buf.len += sprintWchar(
str_buf.array + str_buf.len,
win_rec.Event.KeyEvent.uChar.UnicodeChar,
);
},
windows.MOUSE_EVENT => translateMouse(
rec_buf,
win_rec.Event.MouseEvent,
),
windows.WINDOW_BUFFER_SIZE_EVENT => void,
else => void,
}
}
fn translateMouse(
rec_buf: *input.RecordBuffer,
win_mouse: windows.MOUSE_EVENT_RECORD,
) void {
switch (win_mouse.dwEventFlags) {
windows.MOUSE_MOVED => input.Record.readJoystick(
rec_buf,
0,
win_mouse.dwMousePosition.X,
win_mouse.dwMousePosition.Y,
),
windows.MOUSE_WHEELED => input.Record.readAxis(
rec_buf,
1,
win_mouse.dwButtonState,
),
windows.MOUSE_HWHEELED => input.Record.readAxis(
rec_buf,
2,
win_mouse.dwButtonState,
),
}
}
fn sprintWchar(buf: [*]u8, wchar: u16) usize {
if (wchar < 255) {
buf[0] = wchar;
return 1;
}
if (wchar < 2047) {
buf[0] = 0xC0 + (wchar >> 6);
buf[1] = 0x80 + (wchar & 0x3F);
return 2;
}
buf[0] = 0xE0 + (wchar >> 12);
buf[1] = 0x80 + ((wchar >> 6) & 0x3F);
buf[2] = 0x80 + (wchar & 0x3F);
return 3;
}

View file

@ -1,5 +1,5 @@
0 0
203 844424932407779 1716524569939084500 ec1e1d6dab6581d55ec18dff49107eaf 0 src\main.zig 231 844424932407779 1716824983055731700 4865c7439eede09c6c465fa6b8510223 0 src\main.zig
7757 281474978983955 1715741198000000000 3170fcda94ef1eb8e6ca725dff5e254d 1 std\std.zig 7757 281474978983955 1715741198000000000 3170fcda94ef1eb8e6ca725dff5e254d 1 std\std.zig
23040 281474978983952 1715741198000000000 da66963546b611ee7750a27396b7d1ea 1 std\start.zig 23040 281474978983952 1715741198000000000 da66963546b611ee7750a27396b7d1ea 1 std\start.zig
114777 281474978983653 1715741198000000000 b78cd1771bac0cee1cfa01c556ea1508 1 std\debug.zig 114777 281474978983653 1715741198000000000 b78cd1771bac0cee1cfa01c556ea1508 1 std\debug.zig

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.