summaryrefslogtreecommitdiff
path: root/spreed.c
diff options
context:
space:
mode:
Diffstat (limited to 'spreed.c')
-rwxr-xr-xspreed.c202
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;
+}