Browse Source

SIN: Reformat the whole codebase using tweaked .clang-format

yes, sorry, was actually quite well formatted (clang-format default) ... but its probably just me working on the code ever. So, i will just use my preferences here.
I find it important. visual guy and all other excuses...
master
heck 2 years ago
parent
commit
53575550f0
  1. 37
      .clang-format
  2. 2
      Makefile.conf
  3. 120
      src/base.h
  4. 152
      src/cli_main.c
  5. 289
      src/field.c
  6. 23
      src/field.h
  7. 164
      src/gbuffer.c
  8. 151
      src/gbuffer.h
  9. 426
      src/osc_out.c
  10. 45
      src/osc_out.h
  11. 402
      src/oso.c
  12. 31
      src/oso.h
  13. 1283
      src/sim.c
  14. 11
      src/sim.h
  15. 135
      src/sokol_time.h
  16. 1112
      src/sysmisc.c
  17. 165
      src/sysmisc.h
  18. 1327
      src/term_util.c
  19. 128
      src/term_util.h
  20. 7355
      src/tui_main.c
  21. 65
      src/vmio.c
  22. 67
      src/vmio.h

37
.clang-format

@ -3,3 +3,40 @@ ReflowComments: false
MacroBlockBegin: "^BEGIN_OPERATOR" MacroBlockBegin: "^BEGIN_OPERATOR"
MacroBlockEnd: "^END_OPERATOR" MacroBlockEnd: "^END_OPERATOR"
Language: Cpp
DerivePointerAlignment: true
SortIncludes: Never
PointerAlignment: Left
AlignAfterOpenBracket: AlwaysBreak
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
ColumnLimit: 100
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
PenaltyBreakBeforeFirstCallParameter: 0
PenaltyReturnTypeOnItsOwnLine: 1000000
PenaltyBreakAssignment: 1000000
PenaltyExcessCharacter: 10
IndentCaseLabels: true
IndentWidth: 4
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
SpaceAfterTemplateKeyword: false
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Always
IndentPPDirectives: BeforeHash
IndentExternBlock: Indent
Cpp11BracedListStyle: false

2
Makefile.conf

@ -1,4 +1,4 @@
TARGET=tui_main TARGET=orca_tui
LANG_VERSION=c99 LANG_VERSION=c99

120
src/base.h

@ -9,54 +9,52 @@
// clang or gcc or other // clang or gcc or other
#if defined(__clang__) #if defined(__clang__)
#define ORCA_OPT_MINSIZE __attribute__((minsize)) #define ORCA_OPT_MINSIZE __attribute__((minsize))
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define ORCA_OPT_MINSIZE __attribute__((optimize("Os"))) #define ORCA_OPT_MINSIZE __attribute__((optimize("Os")))
#else #else
#define ORCA_OPT_MINSIZE #define ORCA_OPT_MINSIZE
#endif #endif
// (gcc / clang) or msvc or other // (gcc / clang) or msvc or other
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define ORCA_FORCEINLINE __attribute__((always_inline)) inline #define ORCA_FORCEINLINE __attribute__((always_inline)) inline
#define ORCA_NOINLINE __attribute__((noinline)) #define ORCA_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define ORCA_FORCEINLINE __forceinline #define ORCA_FORCEINLINE __forceinline
#define ORCA_NOINLINE __declspec(noinline) #define ORCA_NOINLINE __declspec(noinline)
#else #else
#define ORCA_FORCEINLINE inline #define ORCA_FORCEINLINE inline
#define ORCA_NOINLINE #define ORCA_NOINLINE
#endif #endif
// (gcc / clang) or other // (gcc / clang) or other
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define ORCA_ASSUME_ALIGNED(_ptr, _alignment) \ #define ORCA_ASSUME_ALIGNED(_ptr, _alignment) __builtin_assume_aligned(_ptr, _alignment)
__builtin_assume_aligned(_ptr, _alignment) #define ORCA_PURE __attribute__((pure))
#define ORCA_PURE __attribute__((pure)) #define ORCA_LIKELY(_x) __builtin_expect(_x, 1)
#define ORCA_LIKELY(_x) __builtin_expect(_x, 1) #define ORCA_UNLIKELY(_x) __builtin_expect(_x, 0)
#define ORCA_UNLIKELY(_x) __builtin_expect(_x, 0) #define ORCA_OK_IF_UNUSED __attribute__((unused))
#define ORCA_OK_IF_UNUSED __attribute__((unused)) #define ORCA_UNREACHABLE __builtin_unreachable()
#define ORCA_UNREACHABLE __builtin_unreachable()
#else #else
#define ORCA_ASSUME_ALIGNED(_ptr, _alignment) (_ptr) #define ORCA_ASSUME_ALIGNED(_ptr, _alignment) (_ptr)
#define ORCA_PURE #define ORCA_PURE
#define ORCA_LIKELY(_x) (_x) #define ORCA_LIKELY(_x) (_x)
#define ORCA_UNLIKELY(_x) (_x) #define ORCA_UNLIKELY(_x) (_x)
#define ORCA_OK_IF_UNUSED #define ORCA_OK_IF_UNUSED
#define ORCA_UNREACHABLE assert(false) #define ORCA_UNREACHABLE assert(false)
#endif #endif
// array count, safer on gcc/clang // array count, safer on gcc/clang
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define ORCA_ASSERT_IS_ARRAY(_array) \ #define ORCA_ASSERT_IS_ARRAY(_array) \
(sizeof(char[1 - 2 * __builtin_types_compatible_p( \ (sizeof(char[1 - 2 * __builtin_types_compatible_p(__typeof(_array), __typeof(&(_array)[0]))]) - \
__typeof(_array), __typeof(&(_array)[0]))]) - \ 1)
1) #define ORCA_ARRAY_COUNTOF(_array) \
#define ORCA_ARRAY_COUNTOF(_array) \ (sizeof(_array) / sizeof((_array)[0]) + ORCA_ASSERT_IS_ARRAY(_array))
(sizeof(_array) / sizeof((_array)[0]) + ORCA_ASSERT_IS_ARRAY(_array))
#else #else
// pray // pray
#define ORCA_ARRAY_COUNTOF(_array) (sizeof(_array) / sizeof(_array[0])) #define ORCA_ARRAY_COUNTOF(_array) (sizeof(_array) / sizeof(_array[0]))
#endif #endif
#define ORCA_Y_MAX UINT16_MAX #define ORCA_Y_MAX UINT16_MAX
@ -76,39 +74,41 @@ typedef ssize_t Isz;
typedef char Glyph; typedef char Glyph;
typedef U8 Mark; typedef U8 Mark;
ORCA_FORCEINLINE static Usz orca_round_up_power2(Usz x) { ORCA_FORCEINLINE static Usz orca_round_up_power2(Usz x)
assert(x <= SIZE_MAX / 2 + 1); {
x -= 1; assert(x <= SIZE_MAX / 2 + 1);
x |= x >> 1; x -= 1;
x |= x >> 2; x |= x >> 1;
x |= x >> 4; x |= x >> 2;
x |= x >> 8; x |= x >> 4;
x |= x >> 16; x |= x >> 8;
x |= x >> 16;
#if SIZE_MAX > UINT32_MAX #if SIZE_MAX > UINT32_MAX
x |= x >> 32; x |= x >> 32;
#endif #endif
return x + 1; return x + 1;
} }
ORCA_OK_IF_UNUSED ORCA_OK_IF_UNUSED
static bool orca_is_valid_glyph(Glyph c) { static bool orca_is_valid_glyph(Glyph c)
if (c >= '0' && c <= '9') {
return true; if (c >= '0' && c <= '9')
if (c >= 'A' && c <= 'Z') return true;
return true; if (c >= 'A' && c <= 'Z')
if (c >= 'a' && c <= 'z') return true;
return true; if (c >= 'a' && c <= 'z')
switch (c) { return true;
case '!': switch (c) {
case '#': case '!':
case '%': case '#':
case '*': case '%':
case '.': case '*':
case ':': case '.':
case ';': case ':':
case '=': case ';':
case '?': case '=':
return true; case '?':
} return true;
return false; }
return false;
} }

152
src/cli_main.c

@ -5,7 +5,8 @@
#include "vmio.h" #include "vmio.h"
#include <getopt.h> #include <getopt.h>
static ORCA_NOINLINE void usage(void) { // clang-format off static ORCA_NOINLINE void usage(void)
{ // clang-format off
fprintf(stderr, fprintf(stderr,
"Usage: cli [options] infile\n\n" "Usage: cli [options] infile\n\n"
"Options:\n" "Options:\n"
@ -16,85 +17,86 @@ fprintf(stderr,
" -h or --help Print this message and exit.\n" " -h or --help Print this message and exit.\n"
);} // clang-format on );} // clang-format on
int main(int argc, char **argv) { int main(int argc, char **argv)
static struct option cli_options[] = {{"help", no_argument, 0, 'h'}, {
{"quiet", no_argument, 0, 'q'}, static struct option cli_options[] = { { "help", no_argument, 0, 'h' },
{NULL, 0, NULL, 0}}; { "quiet", no_argument, 0, 'q' },
{ NULL, 0, NULL, 0 } };
char *input_file = NULL; char *input_file = NULL;
int ticks = 1; int ticks = 1;
bool print_output = true; bool print_output = true;
for (;;) { for (;;) {
int c = getopt_long(argc, argv, "t:qh", cli_options, NULL); int c = getopt_long(argc, argv, "t:qh", cli_options, NULL);
if (c == -1) if (c == -1)
break; break;
switch (c) { switch (c) {
case 't': case 't':
ticks = atoi(optarg); ticks = atoi(optarg);
if (ticks == 0 && strcmp(optarg, "0")) { if (ticks == 0 && strcmp(optarg, "0")) {
fprintf(stderr, fprintf(
"Bad timestep argument %s.\n" stderr,
"Must be 0 or a positive integer.\n", "Bad timestep argument %s.\n"
optarg); "Must be 0 or a positive integer.\n",
return 1; optarg);
} return 1;
break; }
case 'q': break;
print_output = false; case 'q':
break; print_output = false;
case 'h': break;
usage(); case 'h':
return 0; usage();
case '?': return 0;
usage(); case '?':
return 1; usage();
return 1;
}
} }
}
if (optind == argc - 1) { if (optind == argc - 1) {
input_file = argv[optind]; input_file = argv[optind];
} else if (optind < argc - 1) { } else if (optind < argc - 1) {
fprintf(stderr, "Expected only 1 file argument.\n"); fprintf(stderr, "Expected only 1 file argument.\n");
usage(); usage();
return 1; return 1;
} }
if (input_file == NULL) { if (input_file == NULL) {
fprintf(stderr, "No input file.\n"); fprintf(stderr, "No input file.\n");
usage(); usage();
return 1; return 1;
} }
if (ticks < 0) { if (ticks < 0) {
fprintf(stderr, "Time must be >= 0.\n"); fprintf(stderr, "Time must be >= 0.\n");
usage(); usage();
return 1; return 1;
} }
Field field; Field field;
field_init(&field); field_init(&field);
Field_load_error fle = field_load_file(input_file, &field); Field_load_error fle = field_load_file(input_file, &field);
if (fle != Field_load_error_ok) { if (fle != Field_load_error_ok) {
field_deinit(&field);
fprintf(stderr, "File load error: %s.\n", field_load_error_string(fle));
return 1;
}
Mbuf_reusable mbuf_r;
mbuf_reusable_init(&mbuf_r);
mbuf_reusable_ensure_size(&mbuf_r, field.height, field.width);
Oevent_list oevent_list;
oevent_list_init(&oevent_list);
Usz max_ticks = (Usz)ticks;
for (Usz i = 0; i < max_ticks; ++i) {
mbuffer_clear(mbuf_r.buffer, field.height, field.width);
oevent_list_clear(&oevent_list);
orca_run(field.buffer, mbuf_r.buffer, field.height, field.width, i, &oevent_list, 0);
}
mbuf_reusable_deinit(&mbuf_r);
oevent_list_deinit(&oevent_list);
if (print_output)
field_fput(&field, stdout);
field_deinit(&field); field_deinit(&field);
fprintf(stderr, "File load error: %s.\n", field_load_error_string(fle)); return 0;
return 1;
}
Mbuf_reusable mbuf_r;
mbuf_reusable_init(&mbuf_r);
mbuf_reusable_ensure_size(&mbuf_r, field.height, field.width);
Oevent_list oevent_list;
oevent_list_init(&oevent_list);
Usz max_ticks = (Usz)ticks;
for (Usz i = 0; i < max_ticks; ++i) {
mbuffer_clear(mbuf_r.buffer, field.height, field.width);
oevent_list_clear(&oevent_list);
orca_run(field.buffer, mbuf_r.buffer, field.height, field.width, i,
&oevent_list, 0);
}
mbuf_reusable_deinit(&mbuf_r);
oevent_list_deinit(&oevent_list);
if (print_output)
field_fput(&field, stdout);
field_deinit(&field);
return 0;
} }

289
src/field.c

@ -2,156 +2,191 @@
#include "gbuffer.h" #include "gbuffer.h"
#include <ctype.h> #include <ctype.h>
void field_init(Field *f) { void field_init(Field *f)
f->buffer = NULL; {
f->height = 0; f->buffer = NULL;
f->width = 0; f->height = 0;
f->width = 0;
} }
void field_init_fill(Field *f, Usz height, Usz width, Glyph fill_char) { void field_init_fill(Field *f, Usz height, Usz width, Glyph fill_char)
assert(height <= ORCA_Y_MAX && width <= ORCA_X_MAX); {
Usz num_cells = height * width; assert(height <= ORCA_Y_MAX && width <= ORCA_X_MAX);
f->buffer = malloc(num_cells * sizeof(Glyph)); Usz num_cells = height * width;
memset(f->buffer, fill_char, num_cells); f->buffer = malloc(num_cells * sizeof(Glyph));
f->height = (U16)height; memset(f->buffer, fill_char, num_cells);
f->width = (U16)width; f->height = (U16)height;
f->width = (U16)width;
} }
void field_deinit(Field *f) { free(f->buffer); } void field_deinit(Field *f)
{
free(f->buffer);
}
void field_resize_raw(Field *f, Usz height, Usz width) { void field_resize_raw(Field *f, Usz height, Usz width)
assert(height <= ORCA_Y_MAX && width <= ORCA_X_MAX); {
Usz cells = height * width; assert(height <= ORCA_Y_MAX && width <= ORCA_X_MAX);
f->buffer = realloc(f->buffer, cells * sizeof(Glyph)); Usz cells = height * width;
f->height = (U16)height; f->buffer = realloc(f->buffer, cells * sizeof(Glyph));
f->width = (U16)width; f->height = (U16)height;
f->width = (U16)width;
} }
void field_resize_raw_if_necessary(Field *field, Usz height, Usz width) { void field_resize_raw_if_necessary(Field *field, Usz height, Usz width)
if (field->height != height || field->width != width) { {
field_resize_raw(field, height, width); if (field->height != height || field->width != width) {
} field_resize_raw(field, height, width);
}
} }
void field_copy(Field *src, Field *dest) { void field_copy(Field *src, Field *dest)
field_resize_raw_if_necessary(dest, src->height, src->width); {
gbuffer_copy_subrect(src->buffer, dest->buffer, src->height, src->width, field_resize_raw_if_necessary(dest, src->height, src->width);
dest->height, dest->width, 0, 0, 0, 0, src->height, gbuffer_copy_subrect(
src->width); src->buffer,
dest->buffer,
src->height,
src->width,
dest->height,
dest->width,
0,
0,
0,
0,
src->height,
src->width);
} }
static inline bool glyph_char_is_valid(char c) { return c >= '!' && c <= '~'; } static inline bool glyph_char_is_valid(char c)
{
return c >= '!' && c <= '~';
}
void field_fput(Field *f, FILE *stream) { void field_fput(Field *f, FILE *stream)
enum { Column_buffer_count = 4096 }; {
char out_buffer[Column_buffer_count]; enum
Usz f_height = f->height; {
Usz f_width = f->width; Column_buffer_count = 4096
Glyph *f_buffer = f->buffer; };
if (f_width > Column_buffer_count - 2) char out_buffer[Column_buffer_count];
return; Usz f_height = f->height;
for (Usz iy = 0; iy < f_height; ++iy) { Usz f_width = f->width;
Glyph *row_p = f_buffer + f_width * iy; Glyph *f_buffer = f->buffer;
for (Usz ix = 0; ix < f_width; ++ix) { if (f_width > Column_buffer_count - 2)
char c = row_p[ix]; return;
out_buffer[ix] = glyph_char_is_valid(c) ? c : '?'; for (Usz iy = 0; iy < f_height; ++iy) {
Glyph *row_p = f_buffer + f_width * iy;
for (Usz ix = 0; ix < f_width; ++ix) {
char c = row_p[ix];
out_buffer[ix] = glyph_char_is_valid(c) ? c : '?';
}
out_buffer[f_width] = '\n';
out_buffer[f_width + 1] = '\0';
fputs(out_buffer, stream);
} }
out_buffer[f_width] = '\n';
out_buffer[f_width + 1] = '\0';
fputs(out_buffer, stream);
}
} }
Field_load_error field_load_file(char const *filepath, Field *field) { Field_load_error field_load_file(char const *filepath, Field *field)
FILE *file = fopen(filepath, "r"); {
if (file == NULL) { FILE *file = fopen(filepath, "r");
return Field_load_error_cant_open_file; if (file == NULL) {
} return Field_load_error_cant_open_file;
enum { Bufsize = 4096 };
char buf[Bufsize];
Usz first_row_columns = 0;
Usz rows = 0;
for (;;) {
char *s = fgets(buf, Bufsize, file);
if (s == NULL)
break;
if (rows == ORCA_Y_MAX) {
fclose(file);
return Field_load_error_too_many_rows;
}
Usz len = strlen(buf);
if (len == Bufsize - 1 && buf[len - 1] != '\n' && !feof(file)) {
fclose(file);
return Field_load_error_too_many_columns;
} }
enum
{
Bufsize = 4096
};
char buf[Bufsize];
Usz first_row_columns = 0;
Usz rows = 0;
for (;;) { for (;;) {
if (len == 0) char *s = fgets(buf, Bufsize, file);
break; if (s == NULL)
if (!isspace(buf[len - 1])) break;
break; if (rows == ORCA_Y_MAX) {
--len; fclose(file);
} return Field_load_error_too_many_rows;
if (len == 0) }
continue; Usz len = strlen(buf);
if (len >= ORCA_X_MAX) { if (len == Bufsize - 1 && buf[len - 1] != '\n' && !feof(file)) {
fclose(file); fclose(file);
return Field_load_error_too_many_columns; return Field_load_error_too_many_columns;
}
for (;;) {
if (len == 0)
break;
if (!isspace(buf[len - 1]))
break;
--len;
}
if (len == 0)
continue;
if (len >= ORCA_X_MAX) {
fclose(file);
return Field_load_error_too_many_columns;
}
// quick hack until we use a proper scanner
if (rows == 0) {
first_row_columns = len;
} else if (len != first_row_columns) {
fclose(file);
return Field_load_error_not_a_rectangle;
}
field_resize_raw(field, rows + 1, first_row_columns);
Glyph *rowbuff = field->buffer + first_row_columns * rows;
for (Usz i = 0; i < len; ++i) {
char c = buf[i];
rowbuff[i] = glyph_char_is_valid(c) ? c : '.';
}
++rows;
} }
// quick hack until we use a proper scanner fclose(file);
if (rows == 0) { return Field_load_error_ok;
first_row_columns = len;
} else if (len != first_row_columns) {
fclose(file);
return Field_load_error_not_a_rectangle;
}
field_resize_raw(field, rows + 1, first_row_columns);
Glyph *rowbuff = field->buffer + first_row_columns * rows;
for (Usz i = 0; i < len; ++i) {
char c = buf[i];
rowbuff[i] = glyph_char_is_valid(c) ? c : '.';
}
++rows;
}
fclose(file);
return Field_load_error_ok;
} }
char const *field_load_error_string(Field_load_error fle) { char const *field_load_error_string(Field_load_error fle)
char const *errstr = "Unknown"; {
switch (fle) { char const *errstr = "Unknown";
case Field_load_error_ok: switch (fle) {
errstr = "OK"; case Field_load_error_ok:
break; errstr = "OK";
case Field_load_error_cant_open_file: break;
errstr = "Unable to open file"; case Field_load_error_cant_open_file:
break; errstr = "Unable to open file";
case Field_load_error_too_many_columns: break;
errstr = "Grid file has too many columns"; case Field_load_error_too_many_columns:
break; errstr = "Grid file has too many columns";
case Field_load_error_too_many_rows: break;
errstr = "Grid file has too many rows"; case Field_load_error_too_many_rows:
break; errstr = "Grid file has too many rows";
case Field_load_error_no_rows_read: break;
errstr = "Grid file has no rows"; case Field_load_error_no_rows_read:
break; errstr = "Grid file has no rows";
case Field_load_error_not_a_rectangle: break;
errstr = "Grid file is not a rectangle"; case Field_load_error_not_a_rectangle:
break; errstr = "Grid file is not a rectangle";
} break;
return errstr; }
return errstr;
} }
void mbuf_reusable_init(Mbuf_reusable *mbr) { void mbuf_reusable_init(Mbuf_reusable *mbr)
mbr->buffer = NULL; {
mbr->capacity = 0; mbr->buffer = NULL;
mbr->capacity = 0;
} }
void mbuf_reusable_ensure_size(Mbuf_reusable *mbr, Usz height, Usz width) { void mbuf_reusable_ensure_size(Mbuf_reusable *mbr, Usz height, Usz width)
Usz capacity = height * width; {
if (mbr->capacity < capacity) { Usz capacity = height * width;
mbr->buffer = realloc(mbr->buffer, capacity); if (mbr->capacity < capacity) {
mbr->capacity = capacity; mbr->buffer = realloc(mbr->buffer, capacity);
} mbr->capacity = capacity;
}
} }
void mbuf_reusable_deinit(Mbuf_reusable *mbr) { free(mbr->buffer); } void mbuf_reusable_deinit(Mbuf_reusable *mbr)
{
free(mbr->buffer);
}

23
src/field.h

@ -7,8 +7,8 @@
// might want to do. Not used by the VM. // might want to do. Not used by the VM.
typedef struct { typedef struct {
Glyph *buffer; Glyph *buffer;
U16 width, height; U16 width, height;
} Field; } Field;
void field_init(Field *field); void field_init(Field *field);
@ -19,13 +19,14 @@ void field_resize_raw_if_necessary(Field *field, Usz height, Usz width);
void field_copy(Field *src, Field *dest); void field_copy(Field *src, Field *dest);
void field_fput(Field *field, FILE *stream); void field_fput(Field *field, FILE *stream);
typedef enum { typedef enum
Field_load_error_ok = 0, {
Field_load_error_cant_open_file = 1, Field_load_error_ok = 0,
Field_load_error_too_many_columns = 2, Field_load_error_cant_open_file = 1,
Field_load_error_too_many_rows = 3, Field_load_error_too_many_columns = 2,
Field_load_error_no_rows_read = 4, Field_load_error_too_many_rows = 3,
Field_load_error_not_a_rectangle = 5, Field_load_error_no_rows_read = 4,
Field_load_error_not_a_rectangle = 5,
} Field_load_error; } Field_load_error;
Field_load_error field_load_file(char const *filepath, Field *field); Field_load_error field_load_file(char const *filepath, Field *field);
@ -41,8 +42,8 @@ char const *field_load_error_string(Field_load_error fle);
// that functionality. // that functionality.
typedef struct Mbuf_reusable { typedef struct Mbuf_reusable {
Mark *buffer; Mark *buffer;
Usz capacity; Usz capacity;
} Mbuf_reusable; } Mbuf_reusable;
void mbuf_reusable_init(Mbuf_reusable *mbr); void mbuf_reusable_init(Mbuf_reusable *mbr);

164
src/gbuffer.c

@ -1,80 +1,98 @@
#include "gbuffer.h" #include "gbuffer.h"
void gbuffer_copy_subrect(Glyph *src, Glyph *dest, Usz src_height, void gbuffer_copy_subrect(
Usz src_width, Usz dest_height, Usz dest_width, Glyph *src,
Usz src_y, Usz src_x, Usz dest_y, Usz dest_x, Glyph *dest,
Usz height, Usz width) { Usz src_height,
if (src_height <= src_y || src_width <= src_x || dest_height <= dest_y || Usz src_width,
dest_width <= dest_x) Usz dest_height,
return; Usz dest_width,
Usz ny_0 = src_height - src_y; Usz src_y,
Usz ny_1 = dest_height - dest_y; Usz src_x,
Usz ny = height; Usz dest_y,
if (ny_0 < ny) Usz dest_x,
ny = ny_0; Usz height,
if (ny_1 < ny) Usz width)
ny = ny_1; {
if (ny == 0) if (src_height <= src_y || src_width <= src_x || dest_height <= dest_y || dest_width <= dest_x)
return; return;
Usz row_copy_0 = src_width - src_x; Usz ny_0 = src_height - src_y;
Usz row_copy_1 = dest_width - dest_x; Usz ny_1 = dest_height - dest_y;
Usz row_copy = width; Usz ny = height;
if (row_copy_0 < row_copy) if (ny_0 < ny)
row_copy = row_copy_0; ny = ny_0;
if (row_copy_1 < row_copy) if (ny_1 < ny)
row_copy = row_copy_1; ny = ny_1;
Usz copy_bytes = row_copy * sizeof(Glyph); if (ny == 0)
Glyph *src_p = src + src_y * src_width + src_x; return;
Glyph *dest_p = dest + dest_y * dest_width + dest_x; Usz row_copy_0 = src_width - src_x;
Isz src_stride; Usz row_copy_1 = dest_width - dest_x;
Isz dest_stride; Usz row_copy = width;
if (src_y >= dest_y) { if (row_copy_0 < row_copy)
src_stride = (Isz)src_width; row_copy = row_copy_0;
dest_stride = (Isz)dest_width; if (row_copy_1 < row_copy)
} else { row_copy = row_copy_1;
src_p += (ny - 1) * src_width; Usz copy_bytes = row_copy * sizeof(Glyph);
dest_p += (ny - 1) * dest_width; Glyph *src_p = src + src_y * src_width + src_x;
src_stride = -(Isz)src_width; Glyph *dest_p = dest + dest_y * dest_width + dest_x;
dest_stride = -(Isz)dest_width; Isz src_stride;
} Isz dest_stride;
Usz iy = 0; if (src_y >= dest_y) {
for (;;) { src_stride = (Isz)src_width;
memmove(dest_p, src_p, copy_bytes); dest_stride = (Isz)dest_width;
++iy; } else {
if (iy == ny) src_p += (ny - 1) * src_width;
break; dest_p += (ny - 1) * dest_width;
src_p += src_stride; src_stride = -(Isz)src_width;
dest_p += dest_stride; dest_stride = -(Isz)dest_width;
} }
Usz iy = 0;
for (;;) {
memmove(dest_p, src_p, copy_bytes);
++iy;
if (iy == ny)
break;
src_p += src_stride;
dest_p += dest_stride;
}
} }
void gbuffer_fill_subrect(Glyph *gbuffer, Usz f_height, Usz f_width, Usz y, void gbuffer_fill_subrect(
Usz x, Usz height, Usz width, Glyph fill_char) { Glyph *gbuffer,
if (y >= f_height || x >= f_width) Usz f_height,
return; Usz f_width,
Usz rows_0 = f_height - y; Usz y,
Usz rows = height; Usz x,
if (rows_0 < rows) Usz height,
rows = rows_0; Usz width,
if (rows == 0) Glyph fill_char)
return; {
Usz columns_0 = f_width - x; if (y >= f_height || x >= f_width)
Usz columns = width; return;
if (columns_0 < columns) Usz rows_0 = f_height - y;
columns = columns_0; Usz rows = height;
Usz fill_bytes = columns * sizeof(Glyph); if (rows_0 < rows)
Glyph *p = gbuffer + y * f_width + x; rows = rows_0;
Usz iy = 0; if (rows == 0)
for (;;) { return;
memset(p, fill_char, fill_bytes); Usz columns_0 = f_width - x;
++iy; Usz columns = width;
if (iy == rows) if (columns_0 < columns)
break; columns = columns_0;
p += f_width; Usz fill_bytes = columns * sizeof(Glyph);
} Glyph *p = gbuffer + y * f_width + x;
Usz iy = 0;
for (;;) {
memset(p, fill_char, fill_bytes);
++iy;
if (iy == rows)
break;
p += f_width;
}
} }
void mbuffer_clear(Mark *mbuf, Usz height, Usz width) { void mbuffer_clear(Mark *mbuf, Usz height, Usz width)
Usz cleared_size = height * width; {
memset(mbuf, 0, cleared_size); Usz cleared_size = height * width;
memset(mbuf, 0, cleared_size);
} }

151
src/gbuffer.h

@ -1,85 +1,122 @@
#pragma once #pragma once
#include "base.h" #include "base.h"
ORCA_PURE static inline Glyph gbuffer_peek_relative(Glyph *gbuf, Usz height, ORCA_PURE static inline Glyph gbuffer_peek_relative(
Usz width, Usz y, Usz x, Glyph *gbuf,
Isz delta_y, Isz delta_x) { Usz height,
Isz y0 = (Isz)y + delta_y; Usz width,
Isz x0 = (Isz)x + delta_x; Usz y,
if (y0 < 0 || x0 < 0 || (Usz)y0 >= height || (Usz)x0 >= width) Usz x,
return '.'; Isz delta_y,
return gbuf[(Usz)y0 * width + (Usz)x0]; Isz delta_x)
{
Isz y0 = (Isz)y + delta_y;
Isz x0 = (Isz)x + delta_x;
if (y0 < 0 || x0 < 0 || (Usz)y0 >= height || (Usz)x0 >= width)
return '.';
return gbuf[(Usz)y0 * width + (Usz)x0];
} }
static inline void gbuffer_poke(Glyph *gbuf, Usz height, Usz width, Usz y, static inline void gbuffer_poke(Glyph *gbuf, Usz height, Usz width, Usz y, Usz x, Glyph g)
Usz x, Glyph g) { {
assert(y < height && x < width); assert(y < height && x < width);
(void)height; (void)height;
gbuf[y * width + x] = g; gbuf[y * width + x] = g;
} }
static inline void gbuffer_poke_relative(Glyph *gbuf, Usz height, Usz width, static inline void gbuffer_poke_relative(
Usz y, Usz x, Isz delta_y, Isz delta_x, Glyph *gbuf,
Glyph g) { Usz height,
Isz y0 = (Isz)y + delta_y; Usz width,
Isz x0 = (Isz)x + delta_x; Usz y,
if (y0 < 0 || x0 < 0 || (Usz)y0 >= height || (Usz)x0 >= width) Usz x,
return; Isz delta_y,
gbuf[(Usz)y0 * width + (Usz)x0] = g; Isz delta_x,
Glyph g)
{
Isz y0 = (Isz)y + delta_y;
Isz x0 = (Isz)x + delta_x;
if (y0 < 0 || x0 < 0 || (Usz)y0 >= height || (Usz)x0 >= width)
return;
gbuf[(Usz)y0 * width + (Usz)x0] = g;
} }
ORCA_NOINLINE ORCA_NOINLINE
void gbuffer_copy_subrect(Glyph *src, Glyph *dest, Usz src_grid_h, void gbuffer_copy_subrect(
Usz src_grid_w, Usz dest_grid_h, Usz dest_grid_w, Glyph *src,
Usz src_y, Usz src_x, Usz dest_y, Usz dest_x, Glyph *dest,
Usz height, Usz width); Usz src_grid_h,
Usz src_grid_w,
Usz dest_grid_h,
Usz dest_grid_w,
Usz src_y,
Usz src_x,
Usz dest_y,
Usz dest_x,
Usz height,
Usz width);
ORCA_NOINLINE ORCA_NOINLINE
void gbuffer_fill_subrect(Glyph *gbuf, Usz grid_h, Usz grid_w, Usz y, Usz x, void gbuffer_fill_subrect(
Usz height, Usz width, Glyph fill_char); Glyph *gbuf,
Usz grid_h,
Usz grid_w,
Usz y,
Usz x,
Usz height,
Usz width,
Glyph fill_char);
typedef enum { typedef enum
Mark_flag_none = 0, {
Mark_flag_input = 1 << 0, Mark_flag_none = 0,
Mark_flag_output = 1 << 1, Mark_flag_input = 1 << 0,
Mark_flag_haste_input = 1 << 2, Mark_flag_output = 1 << 1,
Mark_flag_lock = 1 << 3, Mark_flag_haste_input = 1 << 2,
Mark_flag_sleep = 1 << 4, Mark_flag_lock = 1 << 3,
Mark_flag_sleep = 1 << 4,
} Mark_flags; } Mark_flags;
ORCA_OK_IF_UNUSED ORCA_OK_IF_UNUSED
static Mark_flags mbuffer_peek(Mark *mbuf, Usz height, Usz width, Usz y, static Mark_flags mbuffer_peek(Mark *mbuf, Usz height, Usz width, Usz y, Usz x)
Usz x) { {
(void)height; (void)height;
return mbuf[y * width + x]; return mbuf[y * width + x];
} }
ORCA_OK_IF_UNUSED ORCA_OK_IF_UNUSED
static Mark_flags mbuffer_peek_relative(Mark *mbuf, Usz height, Usz width, static Mark_flags mbuffer_peek_relative(Mark *mbuf, Usz height, Usz width, Usz y, Usz x, Isz offs_y, Isz offs_x)
Usz y, Usz x, Isz offs_y, Isz offs_x) { {
Isz y0 = (Isz)y + offs_y; Isz y0 = (Isz)y + offs_y;
Isz x0 = (Isz)x + offs_x; Isz x0 = (Isz)x + offs_x;
if (y0 >= (Isz)height || x0 >= (Isz)width || y0 < 0 || x0 < 0) if (y0 >= (Isz)height || x0 >= (Isz)width || y0 < 0 || x0 < 0)
return Mark_flag_none; return Mark_flag_none;
return mbuf[(Usz)y0 * width + (Usz)x0]; return mbuf[(Usz)y0 * width + (Usz)x0];
} }
ORCA_OK_IF_UNUSED ORCA_OK_IF_UNUSED
static void mbuffer_poke_flags_or(Mark *mbuf, Usz height, Usz width, Usz y, static void mbuffer_poke_flags_or(Mark *mbuf, Usz height, Usz width, Usz y, Usz x, Mark_flags flags)
Usz x, Mark_flags flags) { {
(void)height; (void)height;
mbuf[y * width + x] |= (Mark)flags; mbuf[y * width + x] |= (Mark)flags;
} }
ORCA_OK_IF_UNUSED ORCA_OK_IF_UNUSED
static void mbuffer_poke_relative_flags_or(Mark *mbuf, Usz height, Usz width, static void mbuffer_poke_relative_flags_or(
Usz y, Usz x, Isz offs_y, Isz offs_x, Mark *mbuf,
Mark_flags flags) { Usz height,
Isz y0 = (Isz)y + offs_y; Usz width,
Isz x0 = (Isz)x + offs_x; Usz y,
if (y0 >= (Isz)height || x0 >= (Isz)width || y0 < 0 || x0 < 0) Usz x,
return; Isz offs_y,
mbuf[(Usz)y0 * width + (Usz)x0] |= (Mark)flags; Isz offs_x,
Mark_flags flags)
{
Isz y0 = (Isz)y + offs_y;
Isz x0 = (Isz)x + offs_x;
if (y0 >= (Isz)height || x0 >= (Isz)width || y0 < 0 || x0 < 0)
return;
mbuf[(Usz)y0 * width + (Usz)x0] |= (Mark)flags;
} }
void mbuffer_clear(Mark *mbuf, Usz height, Usz width); void mbuffer_clear(Mark *mbuf, Usz height, Usz width);

426
src/osc_out.c

@ -8,51 +8,50 @@
#include <sys/types.h> #include <sys/types.h>
struct Oosc_dev { struct Oosc_dev {
int fd; int fd;
// Just keep the whole list around, since juggling the strict-aliasing // Just keep the whole list around, since juggling the strict-aliasing
// problems with sockaddr_storage is not worth it. // problems with sockaddr_storage is not worth it.
struct addrinfo *chosen; struct addrinfo *chosen;
struct addrinfo *head; struct addrinfo *head;
}; };
Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev **out_ptr, Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev **out_ptr, char const *dest_addr, char const *dest_port)
char const *dest_addr, {
char const *dest_port) { struct addrinfo hints = { 0 };
struct addrinfo hints = {0}; hints.ai_family = AF_UNSPEC;
hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM;
hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0;
hints.ai_protocol = 0; hints.ai_flags = AI_ADDRCONFIG;
hints.ai_flags = AI_ADDRCONFIG; struct addrinfo *chosen = NULL;
struct addrinfo *chosen = NULL; struct addrinfo *head = NULL;
struct addrinfo *head = NULL; int err = getaddrinfo(dest_addr, dest_port, &hints, &head);
int err = getaddrinfo(dest_addr, dest_port, &hints, &head); if (err != 0) {
if (err != 0) {
#if 0 #if 0
fprintf(stderr, "Failed to get address info, error: %d\n", errno); fprintf(stderr, "Failed to get address info, error: %d\n", errno);
#endif #endif
return Oosc_udp_create_error_getaddrinfo_failed; return Oosc_udp_create_error_getaddrinfo_failed;
} }
// Special behavior: if no hostname was provided, we'll get loopback(s) from // Special behavior: if no hostname was provided, we'll get loopback(s) from
// getaddrinfo. Which is good. But on systems with both an ipv4 and ipv6 // getaddrinfo. Which is good. But on systems with both an ipv4 and ipv6
// address, we might get the ipv6 address listed first. And the OSC server, // address, we might get the ipv6 address listed first. And the OSC server,
// for example Plogue Bidule, might not support ipv6. And defaulting to the // for example Plogue Bidule, might not support ipv6. And defaulting to the
// ipv6 loopback wouldn't work, in that case. So if there's no hostname, and // ipv6 loopback wouldn't work, in that case. So if there's no hostname, and
// we find an ipv4 address in the results, prefer to use that. // we find an ipv4 address in the results, prefer to use that.
// //
// Actually let's just prefer ipv4 completely for now // Actually let's just prefer ipv4 completely for now
#if 0 #if 0
if (!dest_addr) { if (!dest_addr) {
#endif #endif
{ {
for (struct addrinfo *a = head; a; a = a->ai_next) { for (struct addrinfo *a = head; a; a = a->ai_next) {
if (a->ai_family != AF_INET) if (a->ai_family != AF_INET)
continue; continue;
chosen = a; chosen = a;
break; break;
}
} }
} if (!chosen)
if (!chosen) chosen = head;
chosen = head;
#if 0 #if 0
for (struct addrinfo* a = head; a; a = a->ai_next) { for (struct addrinfo* a = head; a; a = a->ai_next) {
char buff[INET6_ADDRSTRLEN]; char buff[INET6_ADDRSTRLEN];
@ -71,34 +70,34 @@ Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev **out_ptr,
} }
} }
#endif #endif
int udpfd = int udpfd = socket(chosen->ai_family, chosen->ai_socktype, chosen->ai_protocol);
socket(chosen->ai_family, chosen->ai_socktype, chosen->ai_protocol); if (udpfd < 0) {
if (udpfd < 0) {
#if 0 #if 0
fprintf(stderr, "Failed to open UDP socket, error number: %d\n", errno); fprintf(stderr, "Failed to open UDP socket, error number: %d\n", errno);
#endif #endif
freeaddrinfo(head); freeaddrinfo(head);
return Oosc_udp_create_error_couldnt_open_socket; return Oosc_udp_create_error_couldnt_open_socket;
} }
Oosc_dev *dev = malloc(sizeof(Oosc_dev)); Oosc_dev *dev = malloc(sizeof(Oosc_dev));
dev->fd = udpfd; dev->fd = udpfd;
dev->chosen = chosen; dev->chosen = chosen;
dev->head = head; dev->head = head;
*out_ptr = dev; *out_ptr = dev;
return Oosc_udp_create_error_ok; return Oosc_udp_create_error_ok;
} }
void oosc_dev_destroy(Oosc_dev *dev) { void oosc_dev_destroy(Oosc_dev *dev)
close(dev->fd); {
freeaddrinfo(dev->head); close(dev->fd);
free(dev); freeaddrinfo(dev->head);
free(dev);
} }
void oosc_send_datagram(Oosc_dev *dev, char const *data, Usz size) { void oosc_send_datagram(Oosc_dev *dev, char const *data, Usz size)
ssize_t res = sendto(dev->fd, data, size, 0, dev->chosen->ai_addr, {
dev->chosen->ai_addrlen); ssize_t res = sendto(dev->fd, data, size, 0, dev->chosen->ai_addr, dev->chosen->ai_addrlen);
(void)res; (void)res;
// TODO handle this in UI somehow // TODO handle this in UI somehow
#if 0 #if 0
if (res < 0) { if (res < 0) {
fprintf(stderr, "UDP message send failed\n"); fprintf(stderr, "UDP message send failed\n");
@ -107,167 +106,188 @@ void oosc_send_datagram(Oosc_dev *dev, char const *data, Usz size) {
#endif #endif
} }
static bool oosc_write_strn(char *restrict buffer, Usz buffer_size, static bool oosc_write_strn(
Usz *buffer_pos, char const *restrict in_str, char *restrict buffer,
Usz in_str_len) { Usz buffer_size,
// no overflow check, should be fine Usz *buffer_pos,
Usz in_plus_null = in_str_len + 1; char const *restrict in_str,
Usz null_pad = (4 - in_plus_null % 4) % 4; Usz in_str_len)
Usz needed = in_plus_null + null_pad; {
Usz cur_pos = *buffer_pos; // no overflow check, should be fine
if (cur_pos + needed >= buffer_size) Usz in_plus_null = in_str_len + 1;
return false; Usz null_pad = (4 - in_plus_null % 4) % 4;
for (Usz i = 0; i < in_str_len; ++i) { Usz needed = in_plus_null + null_pad;
buffer[cur_pos + i] = in_str[i]; Usz cur_pos = *buffer_pos;
} if (cur_pos + needed >= buffer_size)
buffer[cur_pos + in_str_len] = 0; return false;
cur_pos += in_plus_null; for (Usz i = 0; i < in_str_len; ++i) {
for (Usz i = 0; i < null_pad; ++i) { buffer[cur_pos + i] = in_str[i];
buffer[cur_pos + i] = 0; }
} buffer[cur_pos + in_str_len] = 0;
*buffer_pos = cur_pos + null_pad; cur_pos += in_plus_null;
return true; for (Usz i = 0; i < null_pad; ++i) {
buffer[cur_pos + i] = 0;
}
*buffer_pos = cur_pos + null_pad;
return true;
} }
void oosc_send_int32s(Oosc_dev *dev, char const *osc_address, I32 const *vals, void oosc_send_int32s(Oosc_dev *dev, char const *osc_address, I32 const *vals, Usz count)
Usz count) { {
char buffer[2048]; char buffer[2048];
Usz buf_pos = 0; Usz buf_pos = 0;
if (!oosc_write_strn(buffer, sizeof(buffer), &buf_pos, osc_address, if (!oosc_write_strn(buffer, sizeof(buffer), &buf_pos, osc_address, strlen(osc_address)))
strlen(osc_address))) return;
return; Usz typetag_str_size = 1 + count + 1; // comma, 'i'... , null
Usz typetag_str_size = 1 + count + 1; // comma, 'i'... , null Usz typetag_str_null_pad = (4 - typetag_str_size % 4) % 4;
Usz typetag_str_null_pad = (4 - typetag_str_size % 4) % 4; if (buf_pos + typetag_str_size + typetag_str_null_pad > sizeof(buffer))
if (buf_pos + typetag_str_size + typetag_str_null_pad > sizeof(buffer)) return;
return; buffer[buf_pos] = ',';
buffer[buf_pos] = ','; ++buf_pos;
++buf_pos; for (Usz i = 0; i < count; ++i) {
for (Usz i = 0; i < count; ++i) { buffer[buf_pos + i] = 'i';
buffer[buf_pos + i] = 'i'; }
} buffer[buf_pos + count] = 0;
buffer[buf_pos + count] = 0; buf_pos += count + 1;
buf_pos += count + 1; for (Usz i = 0; i < typetag_str_null_pad; ++i) {
for (Usz i = 0; i < typetag_str_null_pad; ++i) { buffer[buf_pos + i] = 0;
buffer[buf_pos + i] = 0; }
} buf_pos += typetag_str_null_pad;
buf_pos += typetag_str_null_pad; Usz ints_size = count * sizeof(I32);
Usz ints_size = count * sizeof(I32); if (buf_pos + ints_size > sizeof(buffer))
if (buf_pos + ints_size > sizeof(buffer)) return;
return; for (Usz i = 0; i < count; ++i) {
for (Usz i = 0; i < count; ++i) { union {
union { I32 i;
I32 i; U32 u;
U32 u; } pun;
} pun; pun.i = vals[i];
pun.i = vals[i]; U32 u_ne = htonl(pun.u);
U32 u_ne = htonl(pun.u); memcpy(buffer + buf_pos, &u_ne, sizeof(u_ne));
memcpy(buffer + buf_pos, &u_ne, sizeof(u_ne)); buf_pos += sizeof(u_ne);
buf_pos += sizeof(u_ne); }
} oosc_send_datagram(dev, buffer, buf_pos);
oosc_send_datagram(dev, buffer, buf_pos);
} }
void susnote_list_init(Susnote_list *sl) { void susnote_list_init(Susnote_list *sl)
sl->buffer = NULL; {
sl->count = 0; sl->buffer = NULL;
sl->capacity = 0; sl->count = 0;
sl->capacity = 0;
} }
void susnote_list_deinit(Susnote_list *sl) { free(sl->buffer); } void susnote_list_deinit(Susnote_list *sl)
{
free(sl->buffer);
}
void susnote_list_clear(Susnote_list *sl) { sl->count = 0; } void susnote_list_clear(Susnote_list *sl)
{
sl->count = 0;
}
void susnote_list_add_notes(Susnote_list *sl, Susnote const *restrict notes, void susnote_list_add_notes(
Usz added_count, Usz *restrict start_removed, Susnote_list *sl,
Usz *restrict end_removed) { Susnote const *restrict notes,
Susnote *buffer = sl->buffer; Usz added_count,
Usz count = sl->count; Usz *restrict start_removed,
Usz cap = sl->capacity; Usz *restrict end_removed)
Usz rem = count + added_count; {
Usz needed_cap = rem + added_count; Susnote *buffer = sl->buffer;
if (cap < needed_cap) { Usz count = sl->count;
cap = needed_cap < 16 ? 16 : orca_round_up_power2(needed_cap); Usz cap = sl->capacity;
buffer = realloc(buffer, cap * sizeof(Susnote)); Usz rem = count + added_count;
sl->capacity = cap; Usz needed_cap = rem + added_count;
sl->buffer = buffer; if (cap < needed_cap) {
} cap = needed_cap < 16 ? 16 : orca_round_up_power2(needed_cap);
*start_removed = rem; buffer = realloc(buffer, cap * sizeof(Susnote));
Usz i_in = 0; sl->capacity = cap;
for (; i_in < added_count; ++i_in) { sl->buffer = buffer;
Susnote this_in = notes[i_in];
for (Usz i_old = 0; i_old < count; ++i_old) {
Susnote this_old = buffer[i_old];
if (this_old.chan_note == this_in.chan_note) {
buffer[i_old] = this_in;
buffer[rem] = this_old;
++rem;
goto next_in;
}
} }
buffer[count] = this_in; *start_removed = rem;
++count; Usz i_in = 0;
next_in:; for (; i_in < added_count; ++i_in) {
} Susnote this_in = notes[i_in];
sl->count = count; for (Usz i_old = 0; i_old < count; ++i_old) {
*end_removed = rem; Susnote this_old = buffer[i_old];
if (this_old.chan_note == this_in.chan_note) {
buffer[i_old] = this_in;
buffer[rem] = this_old;
++rem;
goto next_in;
}
}
buffer[count] = this_in;
++count;
next_in:;
}
sl->count = count;
*end_removed = rem;
} }
void susnote_list_advance_time(Susnote_list *sl, double delta_time, void susnote_list_advance_time(
Usz *restrict start_removed, Susnote_list *sl,
Usz *restrict end_removed, double delta_time,
double *soonest_deadline) { Usz *restrict start_removed,
Susnote *restrict buffer = sl->buffer; Usz *restrict end_removed,
Usz count = sl->count; double *soonest_deadline)
*end_removed = count; {
float delta_float = (float)delta_time; Susnote *restrict buffer = sl->buffer;
float soonest = 1.0f; Usz count = sl->count;
for (Usz i = 0; i < count;) { *end_removed = count;
Susnote sn = buffer[i]; float delta_float = (float)delta_time;
sn.remaining -= delta_float; float soonest = 1.0f;
if (sn.remaining > 0.001) { for (Usz i = 0; i < count;) {
if (sn.remaining < soonest) Susnote sn = buffer[i];
soonest = sn.remaining; sn.remaining -= delta_float;
buffer[i].remaining = sn.remaining; if (sn.remaining > 0.001) {
++i; if (sn.remaining < soonest)
} else { soonest = sn.remaining;
--count; buffer[i].remaining = sn.remaining;
buffer[i] = buffer[count]; ++i;
buffer[count] = sn; } else {
--count;
buffer[i] = buffer[count];
buffer[count] = sn;
}
} }
} *start_removed = count;
*start_removed = count; *soonest_deadline = (double)soonest;
*soonest_deadline = (double)soonest; sl->count = count;
sl->count = count;
} }
void susnote_list_remove_by_chan_mask(Susnote_list *sl, Usz chan_mask, void susnote_list_remove_by_chan_mask(
Usz *restrict start_removed, Susnote_list *sl,
Usz *restrict end_removed) { Usz chan_mask,
Susnote *restrict buffer = sl->buffer; Usz *restrict start_removed,
Usz count = sl->count; Usz *restrict end_removed)
*end_removed = count; {
for (Usz i = 0; i < count;) { Susnote *restrict buffer = sl->buffer;
Susnote sn = buffer[i]; Usz count = sl->count;
Usz chan = sn.chan_note >> 8; *end_removed = count;
if (chan_mask & 1u << chan) { for (Usz i = 0; i < count;) {
--count; Susnote sn = buffer[i];
buffer[i] = buffer[count]; Usz chan = sn.chan_note >> 8;
buffer[count] = sn; if (chan_mask & 1u << chan) {
} else { --count;
++i; buffer[i] = buffer[count];
buffer[count] = sn;
} else {
++i;
}
} }
} *start_removed = count;
*start_removed = count; sl->count = count;
sl->count = count;
} }
double susnote_list_soonest_deadline(Susnote_list const *sl) { double susnote_list_soonest_deadline(Susnote_list const *sl)
float soonest = 1.0f; {
Susnote const *buffer = sl->buffer; float soonest = 1.0f;
for (Usz i = 0, n = sl->count; i < n; ++i) { Susnote const *buffer = sl->buffer;
float rem = buffer[i].remaining; for (Usz i = 0, n = sl->count; i < n; ++i) {
if (rem < soonest) float rem = buffer[i].remaining;
soonest = rem; if (rem < soonest)
} soonest = rem;
return (double)soonest; }
return (double)soonest;
} }

45
src/osc_out.h

@ -3,15 +3,14 @@
typedef struct Oosc_dev Oosc_dev; typedef struct Oosc_dev Oosc_dev;
typedef enum { typedef enum
Oosc_udp_create_error_ok = 0, {
Oosc_udp_create_error_getaddrinfo_failed = 1, Oosc_udp_create_error_ok = 0,
Oosc_udp_create_error_couldnt_open_socket = 2, Oosc_udp_create_error_getaddrinfo_failed = 1,
Oosc_udp_create_error_couldnt_open_socket = 2,
} Oosc_udp_create_error; } Oosc_udp_create_error;
Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev **out_ptr, Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev **out_ptr, char const *dest_addr, char const *dest_port);
char const *dest_addr,
char const *dest_port);
void oosc_dev_destroy(Oosc_dev *dev); void oosc_dev_destroy(Oosc_dev *dev);
// Send a raw UDP datagram. // Send a raw UDP datagram.
@ -19,8 +18,7 @@ void oosc_send_datagram(Oosc_dev *dev, char const *data, Usz size);
// Send a list/array of 32-bit integers in OSC format to the specified "osc // Send a list/array of 32-bit integers in OSC format to the specified "osc
// address" (a path like /foo) as a UDP datagram. // address" (a path like /foo) as a UDP datagram.
void oosc_send_int32s(Oosc_dev *dev, char const *osc_address, I32 const *vals, void oosc_send_int32s(Oosc_dev *dev, char const *osc_address, I32 const *vals, Usz count);
Usz count);
// Susnote is for handling MIDI note sustains -- each MIDI on event should be // Susnote is for handling MIDI note sustains -- each MIDI on event should be
// matched with a MIDI note-off event. The duration/sustain length of a MIDI // matched with a MIDI note-off event. The duration/sustain length of a MIDI
@ -28,29 +26,36 @@ void oosc_send_int32s(Oosc_dev *dev, char const *osc_address, I32 const *vals,
// responsible for sending the note-off event. We keep a list of currently 'on' // responsible for sending the note-off event. We keep a list of currently 'on'
// notes so that they can have a matching 'off' sent at the correct time. // notes so that they can have a matching 'off' sent at the correct time.
typedef struct { typedef struct {
float remaining; float remaining;
U16 chan_note; U16 chan_note;
} Susnote; } Susnote;
typedef struct { typedef struct {
Susnote *buffer; Susnote *buffer;
Usz count, capacity; Usz count, capacity;
} Susnote_list; } Susnote_list;
void susnote_list_init(Susnote_list *sl); void susnote_list_init(Susnote_list *sl);
void susnote_list_deinit(Susnote_list *sl); void susnote_list_deinit(Susnote_list *sl);
void susnote_list_clear(Susnote_list *sl); void susnote_list_clear(Susnote_list *sl);
void susnote_list_add_notes(Susnote_list *sl, Susnote const *restrict notes, void susnote_list_add_notes(
Usz count, Usz *restrict start_removed, Susnote_list *sl,
Usz *restrict end_removed); Susnote const *restrict notes,
Usz count,
Usz *restrict start_removed,
Usz *restrict end_removed);
void susnote_list_advance_time( void susnote_list_advance_time(
Susnote_list *sl, double delta_time, Usz *restrict start_removed, Susnote_list *sl,
double delta_time,
Usz *restrict start_removed,
Usz *restrict end_removed, Usz *restrict end_removed,
// 1.0 if no notes remain or none are shorter than 1.0 // 1.0 if no notes remain or none are shorter than 1.0
double *soonest_deadline); double *soonest_deadline);
void susnote_list_remove_by_chan_mask(Susnote_list *sl, Usz chan_mask, void susnote_list_remove_by_chan_mask(
Usz *restrict start_removed, Susnote_list *sl,
Usz *restrict end_removed); Usz chan_mask,
Usz *restrict start_removed,
Usz *restrict end_removed);
// Returns 1.0 if no notes remain or none are shorter than 1.0 // Returns 1.0 if no notes remain or none are shorter than 1.0
double susnote_list_soonest_deadline(Susnote_list const *sl); double susnote_list_soonest_deadline(Susnote_list const *sl);

402
src/oso.c

@ -5,16 +5,16 @@
#include <string.h> #include <string.h>
#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) #if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute)
#if __has_attribute(noinline) && __has_attribute(noclone) #if __has_attribute(noinline) && __has_attribute(noclone)
#define OSO_NOINLINE __attribute__((noinline, noclone)) #define OSO_NOINLINE __attribute__((noinline, noclone))
#elif __has_attribute(noinline) #elif __has_attribute(noinline)
#define OSO_NOINLINE __attribute__((noinline)) #define OSO_NOINLINE __attribute__((noinline))
#endif #endif
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define OSO_NOINLINE __declspec(noinline) #define OSO_NOINLINE __declspec(noinline)
#endif #endif
#ifndef OSO_NOINLINE #ifndef OSO_NOINLINE
#define OSO_NOINLINE #define OSO_NOINLINE
#endif #endif
#define OSO_INTERNAL OSO_NOINLINE static #define OSO_INTERNAL OSO_NOINLINE static
@ -22,208 +22,240 @@
#define OSO_CAP_MAX (SIZE_MAX - (sizeof(oso_header) + 1)) #define OSO_CAP_MAX (SIZE_MAX - (sizeof(oso_header) + 1))
typedef struct oso { typedef struct oso {
size_t len, cap; size_t len, cap;
} oso_header; } oso_header;
OSO_INTERNAL oso *oso_impl_reallochdr(oso_header *hdr, size_t new_cap) { OSO_INTERNAL oso *oso_impl_reallochdr(oso_header *hdr, size_t new_cap)
if (hdr) { {
oso_header *new_hdr = realloc(hdr, sizeof(oso_header) + new_cap + 1); if (hdr) {
if (!new_hdr) { oso_header *new_hdr = realloc(hdr, sizeof(oso_header) + new_cap + 1);
free(hdr); if (!new_hdr) {
return NULL; free(hdr);
return NULL;
}
new_hdr->cap = new_cap;
return new_hdr + 1;
} }
new_hdr->cap = new_cap; hdr = malloc(sizeof(oso_header) + new_cap + 1);
return new_hdr + 1; if (!hdr)
} return NULL;
hdr = malloc(sizeof(oso_header) + new_cap + 1); hdr->len = 0;
if (!hdr) hdr->cap = new_cap;
return NULL; ((char *)(hdr + 1))[0] = '\0';
hdr->len = 0; return hdr + 1;
hdr->cap = new_cap; }
((char *)(hdr + 1))[0] = '\0'; OSO_INTERNAL oso *oso_impl_catvprintf(oso *s, char const *fmt, va_list ap)
return hdr + 1; {
} size_t old_len;
OSO_INTERNAL oso *oso_impl_catvprintf(oso *s, char const *fmt, va_list ap) { int required;
size_t old_len; va_list cpy;
int required; va_copy(cpy, ap);
va_list cpy; required = vsnprintf(NULL, 0, fmt, cpy);
va_copy(cpy, ap); va_end(cpy);
required = vsnprintf(NULL, 0, fmt, cpy); osomakeroomfor(&s, (size_t)required);
va_end(cpy); if (!s)
osomakeroomfor(&s, (size_t)required); return NULL;
if (!s) old_len = OSO_HDR(s)->len;
return NULL; vsnprintf((char *)s + old_len, (size_t)required + 1, fmt, ap);
old_len = OSO_HDR(s)->len; OSO_HDR(s)->len = old_len + (size_t)required;
vsnprintf((char *)s + old_len, (size_t)required + 1, fmt, ap); return s;
OSO_HDR(s)->len = old_len + (size_t)required;
return s;
} }
OSO_NOINLINE OSO_NOINLINE
void osoensurecap(oso **p, size_t new_cap) { void osoensurecap(oso **p, size_t new_cap)
oso *s = *p; {
if (new_cap > OSO_CAP_MAX) { oso *s = *p;
if (new_cap > OSO_CAP_MAX) {
if (s) {
free(OSO_HDR(s));
*p = NULL;
}
return;
}
oso_header *hdr = NULL;
if (s) { if (s) {
free(OSO_HDR(s)); hdr = OSO_HDR(s);
*p = NULL; if (hdr->cap >= new_cap)
return;
} }
return; *p = oso_impl_reallochdr(hdr, new_cap);
}
oso_header *hdr = NULL;
if (s) {
hdr = OSO_HDR(s);
if (hdr->cap >= new_cap)
return;
}
*p = oso_impl_reallochdr(hdr, new_cap);
} }
OSO_NOINLINE OSO_NOINLINE
void osomakeroomfor(oso **p, size_t add_len) { void osomakeroomfor(oso **p, size_t add_len)
oso *s = *p; {
oso_header *hdr = NULL; oso *s = *p;
size_t new_cap; oso_header *hdr = NULL;
if (s) { size_t new_cap;
hdr = OSO_HDR(s); if (s) {
size_t len = hdr->len, cap = hdr->cap; hdr = OSO_HDR(s);
if (len > OSO_CAP_MAX - add_len) { // overflow, goodnight size_t len = hdr->len, cap = hdr->cap;
free(hdr); if (len > OSO_CAP_MAX - add_len) { // overflow, goodnight
*p = NULL; free(hdr);
return; *p = NULL;
return;
}
new_cap = len + add_len;
if (cap >= new_cap)
return;
} else {
if (add_len > OSO_CAP_MAX)
return;
new_cap = add_len;
} }
new_cap = len + add_len; *p = oso_impl_reallochdr(hdr, new_cap);
if (cap >= new_cap)
return;
} else {
if (add_len > OSO_CAP_MAX)
return;
new_cap = add_len;
}
*p = oso_impl_reallochdr(hdr, new_cap);
} }
void osoput(oso **p, char const *restrict cstr) { void osoput(oso **p, char const *restrict cstr)
osoputlen(p, cstr, strlen(cstr)); {
osoputlen(p, cstr, strlen(cstr));
} }
OSO_NOINLINE OSO_NOINLINE
void osoputlen(oso **p, char const *restrict cstr, size_t len) { void osoputlen(oso **p, char const *restrict cstr, size_t len)
oso *s = *p; {
osoensurecap(&s, len); oso *s = *p;
if (s) { osoensurecap(&s, len);
OSO_HDR(s)->len = len; if (s) {
memcpy((char *)s, cstr, len); OSO_HDR(s)->len = len;
((char *)s)[len] = '\0'; memcpy((char *)s, cstr, len);
} ((char *)s)[len] = '\0';
*p = s; }
} *p = s;
void osoputoso(oso **p, oso const *other) {
if (!other)
return;
osoputlen(p, (char const *)other, OSO_HDR(other)->len);
}
void osoputvprintf(oso **p, char const *fmt, va_list ap) {
oso *s = *p;
if (s) {
OSO_HDR(s)->len = 0;
((char *)s)[0] = '\0';
}
*p = oso_impl_catvprintf(s, fmt, ap);
} }
void osoputprintf(oso **p, char const *fmt, ...) { void osoputoso(oso **p, oso const *other)
oso *s = *p; {
if (s) { if (!other)
return;
osoputlen(p, (char const *)other, OSO_HDR(other)->len);
}
void osoputvprintf(oso **p, char const *fmt, va_list ap)
{
oso *s = *p;
if (s) {
OSO_HDR(s)->len = 0;
((char *)s)[0] = '\0';
}
*p = oso_impl_catvprintf(s, fmt, ap);
}
void osoputprintf(oso **p, char const *fmt, ...)
{
oso *s = *p;
if (s) {
OSO_HDR(s)->len = 0;
((char *)s)[0] = '\0';
}
va_list ap;
va_start(ap, fmt);
*p = oso_impl_catvprintf(s, fmt, ap);
va_end(ap);
}
void osocat(oso **p, char const *cstr)
{
osocatlen(p, cstr, strlen(cstr));
}
OSO_NOINLINE
void osocatlen(oso **p, char const *cstr, size_t len)
{
oso *s = *p;
osomakeroomfor(&s, len);
if (s) {
oso_header *hdr = OSO_HDR(s);
size_t curr_len = hdr->len;
memcpy((char *)s + curr_len, cstr, len);
((char *)s)[curr_len + len] = '\0';
hdr->len = curr_len + len;
}
*p = s;
}
void osocatoso(oso **p, oso const *other)
{
if (!other)
return;
osocatlen(p, (char const *)other, OSO_HDR(other)->len);
}
void osocatvprintf(oso **p, char const *fmt, va_list ap)
{
*p = oso_impl_catvprintf(*p, fmt, ap);
}
void osocatprintf(oso **p, char const *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
*p = oso_impl_catvprintf(*p, fmt, ap);
va_end(ap);
}
void osoclear(oso **p)
{
oso *s = *p;
if (!s)
return;
OSO_HDR(s)->len = 0; OSO_HDR(s)->len = 0;
((char *)s)[0] = '\0'; ((char *)s)[0] = '\0';
}
va_list ap;
va_start(ap, fmt);
*p = oso_impl_catvprintf(s, fmt, ap);
va_end(ap);
} }
void osocat(oso **p, char const *cstr) { osocatlen(p, cstr, strlen(cstr)); } void osofree(oso *s)
OSO_NOINLINE {
void osocatlen(oso **p, char const *cstr, size_t len) { if (s)
oso *s = *p; free(OSO_HDR(s));
osomakeroomfor(&s, len); }
if (s) { void osowipe(oso **p)
{
osofree(*p);
*p = NULL;
}
void ososwap(oso **a, oso **b)
{
oso *tmp = *a;
*a = *b;
*b = tmp;
}
void osopokelen(oso *s, size_t len)
{
OSO_HDR(s)->len = len;
}
size_t osolen(oso const *s)
{
return s ? OSO_HDR(s)->len : 0;
}
size_t osocap(oso const *s)
{
return s ? OSO_HDR(s)->cap : 0;
}
void osolencap(oso const *s, size_t *out_len, size_t *out_cap)
{
if (!s) {
*out_len = 0;
*out_cap = 0;
return;
}
oso_header *hdr = OSO_HDR(s); oso_header *hdr = OSO_HDR(s);
size_t curr_len = hdr->len; *out_len = hdr->len;
memcpy((char *)s + curr_len, cstr, len); *out_cap = hdr->cap;
((char *)s)[curr_len + len] = '\0'; }
hdr->len = curr_len + len; size_t osoavail(oso const *s)
} {
*p = s; if (!s)
} return 0;
void osocatoso(oso **p, oso const *other) { oso_header *h = OSO_HDR(s);
if (!other) return h->cap - h->len;
return;
osocatlen(p, (char const *)other, OSO_HDR(other)->len);
}
void osocatvprintf(oso **p, char const *fmt, va_list ap) {
*p = oso_impl_catvprintf(*p, fmt, ap);
}
void osocatprintf(oso **p, char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
*p = oso_impl_catvprintf(*p, fmt, ap);
va_end(ap);
}
void osoclear(oso **p) {
oso *s = *p;
if (!s)
return;
OSO_HDR(s)->len = 0;
((char *)s)[0] = '\0';
}
void osofree(oso *s) {
if (s)
free(OSO_HDR(s));
}
void osowipe(oso **p) {
osofree(*p);
*p = NULL;
}
void ososwap(oso **a, oso **b) {
oso *tmp = *a;
*a = *b;
*b = tmp;
}
void osopokelen(oso *s, size_t len) { OSO_HDR(s)->len = len; }
size_t osolen(oso const *s) { return s ? OSO_HDR(s)->len : 0; }
size_t osocap(oso const *s) { return s ? OSO_HDR(s)->cap : 0; }
void osolencap(oso const *s, size_t *out_len, size_t *out_cap) {
if (!s) {
*out_len = 0;
*out_cap = 0;
return;
}
oso_header *hdr = OSO_HDR(s);
*out_len = hdr->len;
*out_cap = hdr->cap;
}
size_t osoavail(oso const *s) {
if (!s)
return 0;
oso_header *h = OSO_HDR(s);
return h->cap - h->len;
} }
void osotrim(oso *restrict s, char const *restrict cut_set) { void osotrim(oso *restrict s, char const *restrict cut_set)
if (!s) {
return; if (!s)
char *str, *end, *start_pos, *end_pos; return;
start_pos = str = (char *)s; char *str, *end, *start_pos, *end_pos;
end_pos = end = str + OSO_HDR(s)->len - 1; start_pos = str = (char *)s;
while (start_pos <= end && strchr(cut_set, *start_pos)) end_pos = end = str + OSO_HDR(s)->len - 1;
start_pos++; while (start_pos <= end && strchr(cut_set, *start_pos))
while (end_pos > start_pos && strchr(cut_set, *end_pos)) start_pos++;
end_pos--; while (end_pos > start_pos && strchr(cut_set, *end_pos))
size_t len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1); end_pos--;
OSO_HDR(s)->len = len; size_t len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1);
if (str != start_pos) OSO_HDR(s)->len = len;
memmove(str, start_pos, len); if (str != start_pos)
str[len] = '\0'; memmove(str, start_pos, len);
str[len] = '\0';
} }
#undef OSO_HDR #undef OSO_HDR

31
src/oso.h

@ -92,18 +92,18 @@
#include <stddef.h> #include <stddef.h>
#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) #if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute)
#if __has_attribute(format) #if __has_attribute(format)
#define OSO_PRINTF(...) __attribute__((format(printf, __VA_ARGS__))) #define OSO_PRINTF(...) __attribute__((format(printf, __VA_ARGS__)))
#endif #endif
#if __has_attribute(nonnull) #if __has_attribute(nonnull)
#define OSO_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) #define OSO_NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
#endif #endif
#endif #endif
#ifndef OSO_PRINTF #ifndef OSO_PRINTF
#define OSO_PRINTF(...) #define OSO_PRINTF(...)
#endif #endif
#ifndef OSO_NONNULL #ifndef OSO_NONNULL
#define OSO_NONNULL(...) #define OSO_NONNULL(...)
#endif #endif
typedef struct oso oso; typedef struct oso oso;
@ -122,19 +122,15 @@ void osoputlen(oso **p, char const *cstr, size_t len) OSO_NONNULL();
void osoputoso(oso **p, oso const *other) OSO_NONNULL(1); void osoputoso(oso **p, oso const *other) OSO_NONNULL(1);
// ^- Same as above, but using another `oso`. `*p` and `other` must not point // ^- Same as above, but using another `oso`. `*p` and `other` must not point
// to overlapping memory. // to overlapping memory.
void osoputvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) void osoputvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) OSO_PRINTF(2, 0);
OSO_PRINTF(2, 0); void osoputprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2) OSO_PRINTF(2, 3);
void osoputprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2)
OSO_PRINTF(2, 3);
// ^- Same as above, but do it by using printf. // ^- Same as above, but do it by using printf.
void osocat(oso **p, char const *cstr) OSO_NONNULL(); void osocat(oso **p, char const *cstr) OSO_NONNULL();
void osocatlen(oso **p, char const *cstr, size_t len) OSO_NONNULL(); void osocatlen(oso **p, char const *cstr, size_t len) OSO_NONNULL();
void osocatoso(oso **p, oso const *other) OSO_NONNULL(1); void osocatoso(oso **p, oso const *other) OSO_NONNULL(1);
void osocatvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) void osocatvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) OSO_PRINTF(2, 0);
OSO_PRINTF(2, 0); void osocatprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2) OSO_PRINTF(2, 3);
void osocatprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2)
OSO_PRINTF(2, 3);
// ^- Append string to oso string. Same rules as `osoput` family. // ^- Append string to oso string. Same rules as `osoput` family.
void osoensurecap(oso **p, size_t cap) OSO_NONNULL(); void osoensurecap(oso **p, size_t cap) OSO_NONNULL();
@ -169,8 +165,7 @@ size_t osolen(oso const *s);
// ^- Bytes in use by the string (not including the '\0' character.) // ^- Bytes in use by the string (not including the '\0' character.)
size_t osocap(oso const *s); size_t osocap(oso const *s);
// ^- Bytes allocated on heap (not including the '\0' terminator.) // ^- Bytes allocated on heap (not including the '\0' terminator.)
void osolencap(oso const *s, size_t *out_len, size_t *out_cap) void osolencap(oso const *s, size_t *out_len, size_t *out_cap) OSO_NONNULL(2, 3);
OSO_NONNULL(2, 3);
// ^- Get both the len and the cap in one call. // ^- Get both the len and the cap in one call.
size_t osoavail(oso const *s); size_t osoavail(oso const *s);
// ^- osocap(s) - osolen(s) // ^- osocap(s) - osolen(s)

1283
src/sim.c

File diff suppressed because it is too large

11
src/sim.h

@ -2,6 +2,11 @@
#include "base.h" #include "base.h"
#include "vmio.h" #include "vmio.h"
void orca_run(Glyph *restrict gbuffer, Mark *restrict mbuffer, Usz height, void orca_run(
Usz width, Usz tick_number, Oevent_list *oevent_list, Glyph *restrict gbuffer,
Usz random_seed); Mark *restrict mbuffer,
Usz height,
Usz width,
Usz tick_number,
Oevent_list *oevent_list,
Usz random_seed);

135
src/sokol_time.h

@ -89,15 +89,15 @@
extern "C" { extern "C" {
#endif #endif
SOKOL_API_DECL void stm_setup(void); SOKOL_API_DECL void stm_setup(void);
SOKOL_API_DECL uint64_t stm_now(void); SOKOL_API_DECL uint64_t stm_now(void);
SOKOL_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); SOKOL_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
SOKOL_API_DECL uint64_t stm_since(uint64_t start_ticks); SOKOL_API_DECL uint64_t stm_since(uint64_t start_ticks);
SOKOL_API_DECL uint64_t stm_laptime(uint64_t* last_time); SOKOL_API_DECL uint64_t stm_laptime(uint64_t *last_time);
SOKOL_API_DECL double stm_sec(uint64_t ticks); SOKOL_API_DECL double stm_sec(uint64_t ticks);
SOKOL_API_DECL double stm_ms(uint64_t ticks); SOKOL_API_DECL double stm_ms(uint64_t ticks);
SOKOL_API_DECL double stm_us(uint64_t ticks); SOKOL_API_DECL double stm_us(uint64_t ticks);
SOKOL_API_DECL double stm_ns(uint64_t ticks); SOKOL_API_DECL double stm_ns(uint64_t ticks);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
@ -106,99 +106,103 @@ SOKOL_API_DECL double stm_ns(uint64_t ticks);
/*-- IMPLEMENTATION ----------------------------------------------------------*/ /*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_IMPL #ifdef SOKOL_IMPL
#ifndef SOKOL_API_IMPL #ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL #define SOKOL_API_IMPL
#endif #endif
#ifndef SOKOL_ASSERT #ifndef SOKOL_ASSERT
#include <assert.h> #include <assert.h>
#define SOKOL_ASSERT(c) assert(c) #define SOKOL_ASSERT(c) assert(c)
#endif #endif
#ifndef _SOKOL_PRIVATE #ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) #if defined(__GNUC__)
#define _SOKOL_PRIVATE __attribute__((unused)) static #define _SOKOL_PRIVATE __attribute__((unused)) static
#else #else
#define _SOKOL_PRIVATE static #define _SOKOL_PRIVATE static
#endif
#endif #endif
#endif
static int _stm_initialized; static int _stm_initialized;
#if defined(_WIN32) #if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>
static LARGE_INTEGER _stm_win_freq; static LARGE_INTEGER _stm_win_freq;
static LARGE_INTEGER _stm_win_start; static LARGE_INTEGER _stm_win_start;
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)
#include <mach/mach_time.h> #include <mach/mach_time.h>
static mach_timebase_info_data_t _stm_osx_timebase; static mach_timebase_info_data_t _stm_osx_timebase;
static uint64_t _stm_osx_start; static uint64_t _stm_osx_start;
#else /* anything else, this will need more care for non-Linux platforms */ #else /* anything else, this will need more care for non-Linux platforms */
#include <time.h> #include <time.h>
static uint64_t _stm_posix_start; static uint64_t _stm_posix_start;
#endif #endif
/* prevent 64-bit overflow when computing relative timestamp /* prevent 64-bit overflow when computing relative timestamp
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
*/ */
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
_SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { _SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom)
{
int64_t q = value / denom; int64_t q = value / denom;
int64_t r = value % denom; int64_t r = value % denom;
return q * numer + r * numer / denom; return q * numer + r * numer / denom;
} }
#endif #endif
SOKOL_API_IMPL void stm_setup(void) { SOKOL_API_IMPL void stm_setup(void)
{
SOKOL_ASSERT(0 == _stm_initialized); SOKOL_ASSERT(0 == _stm_initialized);
_stm_initialized = 1; _stm_initialized = 1;
#if defined(_WIN32) #if defined(_WIN32)
QueryPerformanceFrequency(&_stm_win_freq); QueryPerformanceFrequency(&_stm_win_freq);
QueryPerformanceCounter(&_stm_win_start); QueryPerformanceCounter(&_stm_win_start);
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)
mach_timebase_info(&_stm_osx_timebase); mach_timebase_info(&_stm_osx_timebase);
_stm_osx_start = mach_absolute_time(); _stm_osx_start = mach_absolute_time();
#else #else
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
_stm_posix_start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; _stm_posix_start = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec;
#endif #endif
} }
SOKOL_API_IMPL uint64_t stm_now(void) { SOKOL_API_IMPL uint64_t stm_now(void)
{
SOKOL_ASSERT(_stm_initialized); SOKOL_ASSERT(_stm_initialized);
uint64_t now; uint64_t now;
#if defined(_WIN32) #if defined(_WIN32)
LARGE_INTEGER qpc_t; LARGE_INTEGER qpc_t;
QueryPerformanceCounter(&qpc_t); QueryPerformanceCounter(&qpc_t);
now = int64_muldiv(qpc_t.QuadPart - _stm_win_start.QuadPart, 1000000000, _stm_win_freq.QuadPart); now = int64_muldiv(qpc_t.QuadPart - _stm_win_start.QuadPart, 1000000000, _stm_win_freq.QuadPart);
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)
const uint64_t mach_now = mach_absolute_time() - _stm_osx_start; const uint64_t mach_now = mach_absolute_time() - _stm_osx_start;
now = int64_muldiv(mach_now, _stm_osx_timebase.numer, _stm_osx_timebase.denom); now = int64_muldiv(mach_now, _stm_osx_timebase.numer, _stm_osx_timebase.denom);
#else #else
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm_posix_start; now = ((uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec) - _stm_posix_start;
#endif #endif
return now; return now;
} }
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks)
{
if (new_ticks > old_ticks) { if (new_ticks > old_ticks) {
return new_ticks - old_ticks; return new_ticks - old_ticks;
} } else {
else {
/* FIXME: this should be a value that converts to a non-null double */ /* FIXME: this should be a value that converts to a non-null double */
return 1; return 1;
} }
} }
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks)
{
return stm_diff(stm_now(), start_ticks); return stm_diff(stm_now(), start_ticks);
} }
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { SOKOL_API_IMPL uint64_t stm_laptime(uint64_t *last_time)
{
SOKOL_ASSERT(last_time); SOKOL_ASSERT(last_time);
uint64_t dt = 0; uint64_t dt = 0;
uint64_t now = stm_now(); uint64_t now = stm_now();
@ -209,20 +213,23 @@ SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
return dt; return dt;
} }
SOKOL_API_IMPL double stm_sec(uint64_t ticks) { SOKOL_API_IMPL double stm_sec(uint64_t ticks)
{
return (double)ticks / 1000000000.0; return (double)ticks / 1000000000.0;
} }
SOKOL_API_IMPL double stm_ms(uint64_t ticks) { SOKOL_API_IMPL double stm_ms(uint64_t ticks)
{
return (double)ticks / 1000000.0; return (double)ticks / 1000000.0;
} }
SOKOL_API_IMPL double stm_us(uint64_t ticks) { SOKOL_API_IMPL double stm_us(uint64_t ticks)
{
return (double)ticks / 1000.0; return (double)ticks / 1000.0;
} }
SOKOL_API_IMPL double stm_ns(uint64_t ticks) { SOKOL_API_IMPL double stm_ns(uint64_t ticks)
{
return (double)ticks; return (double)ticks;
} }
#endif /* SOKOL_IMPL */ #endif /* SOKOL_IMPL */

1112
src/sysmisc.c

File diff suppressed because it is too large

165
src/sysmisc.h

@ -5,59 +5,78 @@ struct oso;
void expand_home_tilde(struct oso **path); void expand_home_tilde(struct oso **path);
typedef enum { typedef enum
Cboard_error_none = 0, {
Cboard_error_unavailable, Cboard_error_none = 0,
Cboard_error_popen_failed, Cboard_error_unavailable,
Cboard_error_process_exit_error, Cboard_error_popen_failed,
Cboard_error_process_exit_error,
} Cboard_error; } Cboard_error;
Cboard_error cboard_copy(Glyph const *gbuffer, Usz field_height, Cboard_error cboard_copy(
Usz field_width, Usz rect_y, Usz rect_x, Usz rect_h, Glyph const *gbuffer,
Usz rect_w); Usz field_height,
Usz field_width,
Cboard_error cboard_paste(Glyph *gbuffer, Usz height, Usz width, Usz y, Usz x, Usz rect_y,
Usz *out_h, Usz *out_w); Usz rect_x,
Usz rect_h,
typedef enum { Usz rect_w);
Conf_read_left_and_right = 0, // left and right will be set
Conf_read_irrelevant, // only left will be set Cboard_error cboard_paste(Glyph *gbuffer, Usz height, Usz width, Usz y, Usz x, Usz *out_h, Usz *out_w);
Conf_read_buffer_too_small, // neither will be set
Conf_read_eof, // " typedef enum
Conf_read_io_error, // " {
Conf_read_left_and_right = 0, // left and right will be set
Conf_read_irrelevant, // only left will be set
Conf_read_buffer_too_small, // neither will be set
Conf_read_eof, // "
Conf_read_io_error, // "
} Conf_read_result; } Conf_read_result;
Conf_read_result conf_read_line(FILE *file, char *buf, Usz bufsize, Conf_read_result conf_read_line(
char **out_left, Usz *out_leftlen, FILE *file,
char **out_right, Usz *out_rightlen); char *buf,
Usz bufsize,
bool conf_read_match(FILE **pfile, char const *const *names, Usz nameslen, char **out_left,
char *buf, Usz bufsize, Usz *out_index, char **out_value); Usz *out_leftlen,
char **out_right,
Usz *out_rightlen);
bool conf_read_match(
FILE **pfile,
char const *const *names,
Usz nameslen,
char *buf,
Usz bufsize,
Usz *out_index,
char **out_value);
FILE *conf_file_open_for_reading(char const *conf_file_name); FILE *conf_file_open_for_reading(char const *conf_file_name);
typedef struct { typedef struct {
FILE *origfile, *tempfile; FILE *origfile, *tempfile;
struct oso *canonpath, *temppath; struct oso *canonpath, *temppath;
} Conf_save; } Conf_save;
typedef enum { typedef enum
Conf_save_start_ok = 0, {
Conf_save_start_bad_conf_name, Conf_save_start_ok = 0,
Conf_save_start_alloc_failed, Conf_save_start_bad_conf_name,
Conf_save_start_no_home, Conf_save_start_alloc_failed,
Conf_save_start_mkdir_failed, Conf_save_start_no_home,
Conf_save_start_conf_dir_not_dir, Conf_save_start_mkdir_failed,
Conf_save_start_temp_file_perm_denied, Conf_save_start_conf_dir_not_dir,
Conf_save_start_old_temp_file_stuck, Conf_save_start_temp_file_perm_denied,
Conf_save_start_temp_file_open_failed, Conf_save_start_old_temp_file_stuck,
Conf_save_start_temp_file_open_failed,
} Conf_save_start_error; } Conf_save_start_error;
typedef enum { typedef enum
Conf_save_commit_ok = 0, {
Conf_save_commit_temp_fsync_failed, Conf_save_commit_ok = 0,
Conf_save_commit_temp_close_failed, Conf_save_commit_temp_fsync_failed,
Conf_save_commit_rename_failed, Conf_save_commit_temp_close_failed,
Conf_save_commit_rename_failed,
} Conf_save_commit_error; } Conf_save_commit_error;
Conf_save_start_error conf_save_start(Conf_save *p, char const *conf_file_name); Conf_save_start_error conf_save_start(Conf_save *p, char const *conf_file_name);
@ -85,52 +104,52 @@ Conf_save_commit_error conf_save_commit(Conf_save *p);
// Just playing around with this design // Just playing around with this design
typedef struct { typedef struct {
FILE *file; FILE *file;
Usz index; Usz index;
char *value; char *value;
char buffer[1024]; char buffer[1024];
} Ezconf_r; } Ezconf_r;
void ezconf_r_start(Ezconf_r *ezcr, char const *conf_file_name); void ezconf_r_start(Ezconf_r *ezcr, char const *conf_file_name);
bool ezconf_r_step(Ezconf_r *ezcr, char const *const *names, Usz nameslen); bool ezconf_r_step(Ezconf_r *ezcr, char const *const *names, Usz nameslen);
typedef enum { typedef enum
Ezconf_w_ok = 0, {
Ezconf_w_bad_conf_name, Ezconf_w_ok = 0,
Ezconf_w_oom, Ezconf_w_bad_conf_name,
Ezconf_w_no_home, Ezconf_w_oom,
Ezconf_w_mkdir_failed, Ezconf_w_no_home,
Ezconf_w_conf_dir_not_dir, Ezconf_w_mkdir_failed,
Ezconf_w_old_temp_file_stuck, Ezconf_w_conf_dir_not_dir,
Ezconf_w_temp_file_perm_denied, Ezconf_w_old_temp_file_stuck,
Ezconf_w_temp_open_failed, Ezconf_w_temp_file_perm_denied,
Ezconf_w_temp_fsync_failed, Ezconf_w_temp_open_failed,
Ezconf_w_temp_close_failed, Ezconf_w_temp_fsync_failed,
Ezconf_w_rename_failed, Ezconf_w_temp_close_failed,
Ezconf_w_line_too_long, Ezconf_w_rename_failed,
Ezconf_w_existing_read_error, Ezconf_w_line_too_long,
Ezconf_w_unknown_error, Ezconf_w_existing_read_error,
Ezconf_w_unknown_error,
} Ezconf_w_error; } Ezconf_w_error;
char const *ezconf_w_errorstring(Ezconf_w_error error); char const *ezconf_w_errorstring(Ezconf_w_error error);
typedef struct { typedef struct {
char const *name; char const *name;
intptr_t id; intptr_t id;
uint8_t flags; uint8_t flags;
} Ezconf_opt; } Ezconf_opt;
typedef struct { typedef struct {
Conf_save save; Conf_save save;
Ezconf_opt *opts; Ezconf_opt *opts;
size_t optscount, optscap; size_t optscount, optscap;
intptr_t optid; intptr_t optid;
FILE *file; FILE *file;
Ezconf_w_error error; Ezconf_w_error error;
uint32_t stateflags; uint32_t stateflags;
} Ezconf_w; } Ezconf_w;
void ezconf_w_start(Ezconf_w *ezcw, Ezconf_opt *optsbuffer, size_t buffercap, void ezconf_w_start(Ezconf_w *ezcw, Ezconf_opt *optsbuffer, size_t buffercap, char const *conf_file_name);
char const *conf_file_name);
void ezconf_w_addopt(Ezconf_w *ezcw, char const *key, intptr_t id); void ezconf_w_addopt(Ezconf_w *ezcw, char const *key, intptr_t id);
bool ezconf_w_step(Ezconf_w *ezcw); bool ezconf_w_step(Ezconf_w *ezcw);

1327
src/term_util.c

File diff suppressed because it is too large

128
src/term_util.h

@ -3,116 +3,124 @@
#include <ncurses.h> #include <ncurses.h>
#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) #if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute)
#if __has_attribute(format) #if __has_attribute(format)
#define ORCA_TERM_UTIL_PRINTF(...) __attribute__((format(printf, __VA_ARGS__))) #define ORCA_TERM_UTIL_PRINTF(...) __attribute__((format(printf, __VA_ARGS__)))
#endif #endif
#endif #endif
#ifndef ORCA_TERM_UTIL_PRINTF #ifndef ORCA_TERM_UTIL_PRINTF
#define ORCA_TERM_UTIL_PRINTF(...) #define ORCA_TERM_UTIL_PRINTF(...)
#endif #endif
#define CTRL_PLUS(c) ((c)&037) #define CTRL_PLUS(c) ((c)&037)
struct oso; struct oso;
typedef enum { typedef enum
C_natural, {
C_black, C_natural,
C_red, C_black,
C_green, C_red,
C_yellow, C_green,
C_blue, C_yellow,
C_magenta, C_blue,
C_cyan, C_magenta,
C_white, C_cyan,
C_white,
} Color_name; } Color_name;
enum { enum
Colors_count = C_white + 1, {
Colors_count = C_white + 1,
}; };
enum { enum
Cdef_normal = COLOR_PAIR(1), {
Cdef_normal = COLOR_PAIR(1),
}; };
typedef enum { typedef enum
A_normal = A_NORMAL, {
A_bold = A_BOLD, A_normal = A_NORMAL,
A_dim = A_DIM, A_bold = A_BOLD,
A_standout = A_STANDOUT, A_dim = A_DIM,
A_reverse = A_REVERSE, A_standout = A_STANDOUT,
A_reverse = A_REVERSE,
} Term_attr; } Term_attr;
static ORCA_FORCEINLINE ORCA_OK_IF_UNUSED attr_t fg_bg(Color_name fg, static ORCA_FORCEINLINE ORCA_OK_IF_UNUSED attr_t fg_bg(Color_name fg, Color_name bg)
Color_name bg) { {
return COLOR_PAIR(1 + fg * Colors_count + bg); return COLOR_PAIR(1 + fg * Colors_count + bg);
} }
void term_util_init_colors(void); void term_util_init_colors(void);
typedef enum { typedef enum
Qblock_type_qmsg, {
Qblock_type_qmenu, Qblock_type_qmsg,
Qblock_type_qform, Qblock_type_qmenu,
Qblock_type_qform,
} Qblock_type_tag; } Qblock_type_tag;
typedef struct Qblock { typedef struct Qblock {
Qblock_type_tag tag; Qblock_type_tag tag;
WINDOW *outer_window, *content_window; WINDOW *outer_window, *content_window;
char const *title; char const *title;
struct Qblock *down, *up; struct Qblock *down, *up;
int y, x; int y, x;
} Qblock; } Qblock;
typedef struct { typedef struct {
Qblock *top, *bottom; Qblock *top, *bottom;
bool occlusion_dirty; bool occlusion_dirty;
} Qnav_stack; } Qnav_stack;
typedef struct Qmsg Qmsg; typedef struct Qmsg Qmsg;
typedef struct Qmenu Qmenu; typedef struct Qmenu Qmenu;
typedef enum { typedef enum
Qmenu_action_type_canceled, {
Qmenu_action_type_picked, Qmenu_action_type_canceled,
Qmenu_action_type_picked,
} Qmenu_action_type; } Qmenu_action_type;
typedef struct { typedef struct {
Qmenu_action_type type; Qmenu_action_type type;
} Qmenu_action_any; } Qmenu_action_any;
typedef struct { typedef struct {
Qmenu_action_type type; Qmenu_action_type type;
int id; int id;
} Qmenu_action_picked; } Qmenu_action_picked;
typedef union { typedef union {
Qmenu_action_any any; Qmenu_action_any any;
Qmenu_action_picked picked; Qmenu_action_picked picked;
} Qmenu_action; } Qmenu_action;
typedef struct Qform Qform; typedef struct Qform Qform;
typedef enum { typedef enum
Qform_action_type_canceled, {
Qform_action_type_submitted, Qform_action_type_canceled,
Qform_action_type_submitted,
} Qform_action_type; } Qform_action_type;
typedef struct { typedef struct {
Qform_action_type type; Qform_action_type type;
} Qform_action_any; } Qform_action_any;
typedef union { typedef union {
Qform_action_any any; Qform_action_any any;
} Qform_action; } Qform_action;
typedef enum { typedef enum
Qmsg_dismiss_mode_explicitly, // Space, return, escape dismisses. Default. {
Qmsg_dismiss_mode_easily, // Any key dismisses. Qmsg_dismiss_mode_explicitly, // Space, return, escape dismisses. Default.
Qmsg_dismiss_mode_passthrough, // Easily, and pass through key event. Qmsg_dismiss_mode_easily, // Any key dismisses.
Qmsg_dismiss_mode_passthrough, // Easily, and pass through key event.
} Qmsg_dismiss_mode; } Qmsg_dismiss_mode;
typedef struct { typedef struct {
bool dismiss : 1, passthrough : 1; bool dismiss : 1, passthrough : 1;
} Qmsg_action; } Qmsg_action;
void qnav_init(void); void qnav_init(void);
@ -126,8 +134,7 @@ void qblock_print_frame(Qblock *qb, bool active);
void qblock_set_title(Qblock *qb, char const *title); void qblock_set_title(Qblock *qb, char const *title);
Qmsg *qmsg_push(int height, int width); Qmsg *qmsg_push(int height, int width);
Qmsg *qmsg_printf_push(char const *title, char const *fmt, ...) Qmsg *qmsg_printf_push(char const *title, char const *fmt, ...) ORCA_TERM_UTIL_PRINTF(2, 3);
ORCA_TERM_UTIL_PRINTF(2, 3);
WINDOW *qmsg_window(Qmsg *qm); WINDOW *qmsg_window(Qmsg *qm);
void qmsg_set_title(Qmsg *qm, char const *title); void qmsg_set_title(Qmsg *qm, char const *title);
void qmsg_set_dismiss_mode(Qmsg *qm, Qmsg_dismiss_mode mode); void qmsg_set_dismiss_mode(Qmsg *qm, Qmsg_dismiss_mode mode);
@ -142,8 +149,7 @@ void qmenu_destroy(Qmenu *qm);
int qmenu_id(Qmenu const *qm); int qmenu_id(Qmenu const *qm);
void qmenu_set_title(Qmenu *qm, char const *title); void qmenu_set_title(Qmenu *qm, char const *title);
void qmenu_add_choice(Qmenu *qm, int id, char const *text); void qmenu_add_choice(Qmenu *qm, int id, char const *text);
void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) ORCA_TERM_UTIL_PRINTF(3, 4);
ORCA_TERM_UTIL_PRINTF(3, 4);
void qmenu_add_spacer(Qmenu *qm); void qmenu_add_spacer(Qmenu *qm);
void qmenu_set_current_item(Qmenu *qm, int id); void qmenu_set_current_item(Qmenu *qm, int id);
void qmenu_push_to_nav(Qmenu *qm); void qmenu_push_to_nav(Qmenu *qm);
@ -158,7 +164,7 @@ Qform *qform_of(Qblock *qb);
void qform_set_title(Qform *qf, char const *title); void qform_set_title(Qform *qf, char const *title);
void qform_add_line_input(Qform *qf, int id, char const *initial); void qform_add_line_input(Qform *qf, int id, char const *initial);
void qform_push_to_nav(Qform *qf); void qform_push_to_nav(Qform *qf);
void qform_single_line_input(int id, char const *title, char const* initial); void qform_single_line_input(int id, char const *title, char const *initial);
bool qform_drive(Qform *qf, int key, Qform_action *out_action); bool qform_drive(Qform *qf, int key, Qform_action *out_action);
bool qform_get_text_line(Qform const *qf, int id, struct oso **out); bool qform_get_text_line(Qform const *qf, int id, struct oso **out);
bool qform_get_single_text_line(Qform const *qf, struct oso **out); bool qform_get_single_text_line(Qform const *qf, struct oso **out);

7355
src/tui_main.c

File diff suppressed because it is too large

65
src/vmio.c

@ -1,33 +1,42 @@
#include "vmio.h" #include "vmio.h"
void oevent_list_init(Oevent_list *olist) { void oevent_list_init(Oevent_list *olist)
olist->buffer = NULL; {
olist->count = 0; olist->buffer = NULL;
olist->capacity = 0; olist->count = 0;
olist->capacity = 0;
} }
void oevent_list_deinit(Oevent_list *olist) { free(olist->buffer); } void oevent_list_deinit(Oevent_list *olist)
void oevent_list_clear(Oevent_list *olist) { olist->count = 0; } {
void oevent_list_copy(Oevent_list const *src, Oevent_list *dest) { free(olist->buffer);
Usz src_count = src->count;
if (dest->capacity < src_count) {
Usz new_cap = orca_round_up_power2(src_count);
dest->buffer = realloc(dest->buffer, new_cap * sizeof(Oevent));
dest->capacity = new_cap;
}
memcpy(dest->buffer, src->buffer, src_count * sizeof(Oevent));
dest->count = src_count;
} }
Oevent *oevent_list_alloc_item(Oevent_list *olist) { void oevent_list_clear(Oevent_list *olist)
Usz count = olist->count; {
if (olist->capacity == count) { olist->count = 0;
// Note: no overflow check, but you're probably out of memory if this }
// happens anyway. Like other uses of realloc in orca, we also don't check void oevent_list_copy(Oevent_list const *src, Oevent_list *dest)
// for a failed allocation. {
Usz capacity = count < 16 ? 16 : orca_round_up_power2(count + 1); Usz src_count = src->count;
olist->buffer = realloc(olist->buffer, capacity * sizeof(Oevent)); if (dest->capacity < src_count) {
olist->capacity = capacity; Usz new_cap = orca_round_up_power2(src_count);
} dest->buffer = realloc(dest->buffer, new_cap * sizeof(Oevent));
Oevent *result = olist->buffer + count; dest->capacity = new_cap;
olist->count = count + 1; }
return result; memcpy(dest->buffer, src->buffer, src_count * sizeof(Oevent));
dest->count = src_count;
}
Oevent *oevent_list_alloc_item(Oevent_list *olist)
{
Usz count = olist->count;
if (olist->capacity == count) {
// Note: no overflow check, but you're probably out of memory if this
// happens anyway. Like other uses of realloc in orca, we also don't check
// for a failed allocation.
Usz capacity = count < 16 ? 16 : orca_round_up_power2(count + 1);
olist->buffer = realloc(olist->buffer, capacity * sizeof(Oevent));
olist->capacity = capacity;
}
Oevent *result = olist->buffer + count;
olist->count = count + 1;
return result;
} }

67
src/vmio.h

@ -1,62 +1,69 @@
#pragma once #pragma once
#include "base.h" #include "base.h"
typedef enum { typedef enum
Oevent_type_midi_note, {
Oevent_type_midi_cc, Oevent_type_midi_note,
Oevent_type_midi_pb, Oevent_type_midi_cc,
Oevent_type_osc_ints, Oevent_type_midi_pb,
Oevent_type_udp_string, Oevent_type_osc_ints,
Oevent_type_udp_string,
} Oevent_types; } Oevent_types;
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
} Oevent_any; } Oevent_any;
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
U8 channel, octave, note, velocity, duration : 7, mono : 1; U8 channel, octave, note, velocity, duration : 7, mono : 1;
} Oevent_midi_note; } Oevent_midi_note;
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
U8 channel, control, value; U8 channel, control, value;
} Oevent_midi_cc; } Oevent_midi_cc;
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
U8 channel, lsb, msb; U8 channel, lsb, msb;
} Oevent_midi_pb; } Oevent_midi_pb;
enum { Oevent_osc_int_count = 35 }; enum
{
Oevent_osc_int_count = 35
};
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
Glyph glyph; Glyph glyph;
U8 count; U8 count;
U8 numbers[Oevent_osc_int_count]; U8 numbers[Oevent_osc_int_count];
} Oevent_osc_ints; } Oevent_osc_ints;
enum { Oevent_udp_string_count = 16 }; enum
{
Oevent_udp_string_count = 16
};
typedef struct { typedef struct {
U8 oevent_type; U8 oevent_type;
U8 count; U8 count;
char chars[Oevent_udp_string_count]; char chars[Oevent_udp_string_count];
} Oevent_udp_string; } Oevent_udp_string;
typedef union { typedef union {
Oevent_any any; Oevent_any any;
Oevent_midi_note midi_note; Oevent_midi_note midi_note;
Oevent_midi_cc midi_cc; Oevent_midi_cc midi_cc;
Oevent_midi_pb midi_pb; Oevent_midi_pb midi_pb;
Oevent_osc_ints osc_ints; Oevent_osc_ints osc_ints;
Oevent_udp_string udp_string; Oevent_udp_string udp_string;
} Oevent; } Oevent;
typedef struct { typedef struct {
Oevent *buffer; Oevent *buffer;
Usz count, capacity; Usz count, capacity;
} Oevent_list; } Oevent_list;
void oevent_list_init(Oevent_list *olist); void oevent_list_init(Oevent_list *olist);

Loading…
Cancel
Save