diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2026-04-24 14:54:31 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2026-04-24 14:54:31 -0400 |
| commit | 6a64f5c5223baba53ce8608cc741e61c30c9133a (patch) | |
| tree | dc1d86ec495baa581c6fa10612dc238f94d21e9a /spreed.c | |
| parent | 54a248fba0e37f125a99d436ff73253855f78455 (diff) | |
move src files to project root
Diffstat (limited to 'spreed.c')
| -rwxr-xr-x | spreed.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/spreed.c b/spreed.c new file mode 100755 index 0000000..40376ea --- /dev/null +++ b/spreed.c @@ -0,0 +1,202 @@ +#if 0 +${CC:-gcc} -s -O2 -std=c99 -Wall -o ${SPREED_INSTALL_DIR:-.}/spreed spreed.c +exit +#endif + +#define _POSIX_C_SOURCE 200809L + +#include <locale.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#define SPRD_VER L"0.1" +#define CLR_FG L"\033[31m" +#define CLR_RESET L"\033[0m" +#define SPRD_CLOCK CLOCK_MONOTONIC +#define MS_PER_MIN (60ll * 1000000000ll) + +#define SPRD_CLOCK_DIFF(t1, t2) ((((int64_t)t1.tv_sec * 1000000000ll) + (int64_t)t1.tv_nsec) - (((int64_t)t2.tv_sec * 1000000000ll) + (int64_t)t2.tv_nsec)) + +const uint8_t orps[9] = {0,0,0,1,1,2,2,2,2}; + +uint8_t get_optimal_recognition_point(size_t len) { + if (len >= 10) return 3; + return orps[len-1]; +} + +int main(int argc, char *argv[]) +{ + struct timespec ts_last, ts_current, ts_sleep; + wchar_t word[37]; + wchar_t wc; + char *str; + int64_t cooldown, wpm_ns; + int c; + char chararr[4]; + uint8_t u, uu, ichar, iword, b_word_break; + + setlocale(LC_ALL, ""); + fwide(stdout, 1); + + clock_getres(SPRD_CLOCK, &ts_last); + memset(word, 0, 37 * sizeof(wchar_t)); + cooldown = 0; + wpm_ns = MS_PER_MIN / 250; + memset(chararr, 0, sizeof(chararr)); + ichar = 0; + iword = 0; + b_word_break = 0; + + if (argc > 1) { + if (strstr(argv[1], "-v") || strstr(argv[1], "--v") || strstr(argv[1], "-V") || strstr(argv[1], "--V")) { + fwprintf(stdout, "spreed version "SPRD_VER"\n"); + return 0; + } + } + + str = getenv("SPREED_WPM"); + if (argc > 1) { + str = argv[1]; + } + if (str != NULL) { + wpm_ns = strtoll(str, NULL, 10); + if (wpm_ns == 0) { + fwprintf(stdout, L"Error: parsed '%s' as %ll, setting to default of 250\n", str, wpm_ns); + wpm_ns = 250; + } else if (wpm_ns > (60 * 30)) { + wpm_ns = 60 * 30; + fwprintf(stdout, L"Limiting to %i wpm (30 fps)\n", 60 * 30); + } + wpm_ns = MS_PER_MIN / wpm_ns; + } + + fwprintf(stdout, L"Clock (%i) accuracy: %li.%09lls\n", SPRD_CLOCK, ts_last.tv_sec, ts_last.tv_nsec); + + fwprintf(stdout, L" V\n"); + clock_gettime(SPRD_CLOCK, &ts_current); + ts_last = ts_current; + ts_last.tv_sec -= 60; + do { + while (cooldown > 0) { + clock_gettime(SPRD_CLOCK, &ts_current); + cooldown -= SPRD_CLOCK_DIFF(ts_current, ts_last); + if (cooldown <= 0) { + break; + } + ts_sleep.tv_sec = 0; + ts_sleep.tv_nsec = cooldown > 1000000000ll ? 999999999ll : cooldown; + nanosleep(&ts_sleep, NULL); + } + b_word_break = 0; + c = getchar(); + if (c == EOF) { + b_word_break = iword > 0; + goto END_OF_WORD; + } + chararr[ichar++] = (char)c; + + switch(mbrtowc(&wc, chararr, 4, NULL)) { + case 0: + // null char + if (iword == 0 && ichar == 1) { + ichar = 0; + memset(chararr, 0, 4 * sizeof(char)); + continue; + } else { + b_word_break = 1; + goto END_OF_WORD; + } + case (size_t)-2: + // incomplete wchar_t, keep reading bytes + continue; + case (size_t)-1: + if (ichar == 4) { + // emojis return (size_t)-1 until we have all the bytes + fwprintf(stderr, L"\nmbrtowc encoding error\n"); + return 1; + } + continue; + } + + // not a printable character, swallow + if (!iswprint(wc) && iword == 0) { + ichar = 0; + memset(chararr, 0, 4 * sizeof(char)); + continue; + } + + if (iswspace(wc)) { + if (iword == 0) { + ichar = 0; + memset(chararr, 0, 4 * sizeof(char)); + continue; + } + b_word_break = 1; + } + +END_OF_WORD: + ichar = 0; + memset(chararr, 0, 4 * sizeof(char)); + + if (b_word_break != 0 || iword >= 37) { + if (iword == 0) { + fwprintf(stderr, L"\n0 length word\n"); + return 2; + } + ichar = get_optimal_recognition_point(iword); + uu = 0; + putwchar('\r'); + for (u = 0; u < 48; ++u) { + if (u < 10-ichar) { + putwchar(L' '); + continue; + } + if (uu < iword) { + if (u == 10) { + fwprintf(stdout, L"%ls", CLR_FG); + } + putwchar(word[uu++]); + if (u == 10) { + fwprintf(stdout, L"%ls", CLR_RESET); + } + continue; + } + putwchar(L' '); + } + + fflush(stdout); + + while (iword > 0) { + if (iswalnum(word[iword-1])) { + break; + } + if (word[iword-1] == L'.' || word[iword-1] == L'!' || word[iword-1] == L'?') { + cooldown += wpm_ns; + break; + } + iword--; + } + cooldown += wpm_ns; + + iword = 0; + memset(word, 0, 37 * sizeof(wchar_t)); + ts_last = ts_current; + } + + ichar = 0; + if (!iswspace(wc)) { + word[iword++] = wc; + } + + } while (c != EOF); + + putwchar('\n'); + + return 0; +} |
