From 24b576f59faf55a2aab09409915aae7a93c21d03 Mon Sep 17 00:00:00 2001 From: Lexi Quinn Date: Wed, 29 Sep 2021 05:15:13 +1000 Subject: [PATCH] added non global hotkeys and new display features --- README.md | 19 +++-- src/display.c | 7 +- src/display.h | 3 + src/keys.c | 15 +++- src/keys.h | 11 +-- src/timer.c | 227 ++++++++++++++++++++++++++++++-------------------- src/timer.h | 9 +- 7 files changed, 177 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 3da93c0..299744e 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,15 @@ the names of your game, catagories and splits. Changing split names/number of splits after the first use of the split file is currently unsupported. ### Default Keybinds -| Keys | Action | -| ---- | --------------------- | -| `R` | Start | -| `F` | Stop / Reset | -| `E` | Split | -| `G` | Undo split | -| `V` | Skip split | -| `C` | Close | -| `T` | Toggle global hotkeys | +| Keys | Action | Global | +| ---- | --------------------- | ------ | +| `R` | Start | YES | +| `F` | Stop / Reset | YES | +| `E` | Split | YES | +| `G` | Undo split | YES | +| `V` | Skip split | YES | +| `Q` | Close | NO | +| `T` | Toggle global hotkeys | YES | +| `C` | Toggle compact UI | NO | Customisable hotkeys without editing the source code coming soon! diff --git a/src/display.c b/src/display.c index 52b011e..9ddc413 100644 --- a/src/display.c +++ b/src/display.c @@ -10,6 +10,7 @@ const char *sfulltime = "%4d:%02d.%02d"; int maxrows = INT_MAX; int maxcols = INT_MAX; int colwidth = 10; +int in = 0; struct termios base; @@ -55,6 +56,8 @@ void initScreen(struct color bg, struct color fg) t = base; t.c_lflag &= (~ECHO & ~ICANON); tcsetattr(1, TCSANOW, &t); + dup(0); + fcntl(0, F_SETFL, O_NONBLOCK); altBuffer(); setBGColor(bg); setFGColor(fg); @@ -99,9 +102,9 @@ void drawHLine(int row, int maxlen) //from the right hand side towards the left. void drawColumn(char **data, int count, int column) { - int x = maxcols - (column * 10); + int x = maxcols - (column * 10) + 1; int y = 6; - if (column == 1) + if (column == 0) x = 1; for (int i = 0; i < count; i++) { printf(MVCUR, y + i, x, data[i]); diff --git a/src/display.h b/src/display.h index 51d05d3..2d66c94 100644 --- a/src/display.h +++ b/src/display.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include extern const char *millitime; extern const char *secondstime; @@ -15,6 +17,7 @@ extern const char *sfulltime; extern int maxrows; extern int maxcols; extern int colwidth; +extern int in; struct color { int r; diff --git a/src/keys.c b/src/keys.c index c2257d1..e407e48 100644 --- a/src/keys.c +++ b/src/keys.c @@ -21,8 +21,6 @@ void dispatch_proc(uiohook_event * const event) { buf = K_PAUSE; if (event->data.keyboard.keycode == km.SPLIT) buf = K_SPLIT; - if (event->data.keyboard.keycode == km.CLOSE) - buf = K_CLOSE; if (event->data.keyboard.keycode == km.HOTKS) buf = K_HOTKS; if (event->data.keyboard.keycode == km.USPLT) @@ -38,6 +36,8 @@ void dispatch_proc(uiohook_event * const event) { int handleInput() { ssize_t rd = read(pipefd[0], &buf, 1); + char t; + read(in, &t, 1); if ((!hotkeys_enabled && buf != K_HOTKS) || rd == -1) return 0; if (buf == K_SPLIT) @@ -48,13 +48,20 @@ int handleInput() stop(); if (buf == K_PAUSE) tpause(); - if (buf == K_HOTKS) + if (buf == K_HOTKS) { hotkeys_enabled = !hotkeys_enabled; + if (hotkeys_enabled) + drawNotif("Global hotkeys enabled"); + else + drawNotif("Global hotkeys disabled"); + } if (buf == K_USPLT) unsplit(); if (buf == K_SKIP) skip(); - if (buf == K_CLOSE) + if (t == 'c') + toggleCompact(); + if (t == 'q') return 1; return 0; } diff --git a/src/keys.h b/src/keys.h index 9b62865..dcf112c 100644 --- a/src/keys.h +++ b/src/keys.h @@ -4,15 +4,17 @@ #include #include #include "timer.h" +#include "display.h" #define K_START 1 #define K_STOP 2 #define K_PAUSE 3 #define K_SPLIT 4 -#define K_CLOSE 5 -#define K_HOTKS 6 -#define K_USPLT 7 -#define K_SKIP 8 +#define K_HOTKS 5 +#define K_USPLT 6 +#define K_SKIP 7 + +extern bool hotkeys_enabled; struct keymap { @@ -20,7 +22,6 @@ struct keymap uint16_t STOP; uint16_t PAUSE; uint16_t SPLIT; - uint16_t CLOSE; uint16_t HOTKS; uint16_t USPLT; uint16_t SKIP; diff --git a/src/timer.c b/src/timer.c index bd8259a..db3b945 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,7 +1,7 @@ #include "timer.h" //Timekeeping -struct timespec timestart, finish; +struct timespec timestart, finish, notif; int currentMS = 0; bool timerActive; @@ -9,10 +9,8 @@ bool timerActive; struct color bg = { 47, 53, 66}; struct color fg = {247, 248, 242}; int h, w; -int deltaOn = 1; -int sgmtdurOn = 1; -int pbOn = 1; -bool resized = false; +bool compact = false; +bool dirty = false; //Splits.io data const char *schemaver = "v1.0.1"; @@ -56,7 +54,12 @@ void start() return; clock_gettime(CLOCK_REALTIME, ×tart); timerActive = true; - resized = true; + //Reset state of timer + for(int i = 0; i < segCount; i++) { + segments[i].ms = 0; + segments[i].isSkipped = false; + segments[i].isReset = false; + } currSeg = 0; } @@ -81,14 +84,6 @@ void stop() } calculatePB(); saveFile(); - - //Reset state of timer - for(int i = 0; i < segCount; i++) { - segments[i].ms = 0; - segments[i].isSkipped = false; - segments[i].isReset = false; - } - currSeg = 0; } void split() @@ -117,7 +112,8 @@ void skip() { if (!timerActive) return; - segments[currSeg].isSkipped = true; + if (currSeg < segCount) + segments[currSeg].isSkipped = true; currSeg++; if (currSeg >= segCount) stop(); @@ -129,7 +125,6 @@ void loadKeymap() km.STOP = VC_F; km.PAUSE = VC_D; km.SPLIT = VC_E; - km.CLOSE = VC_C; km.HOTKS = VC_T; km.USPLT = VC_G; km.SKIP = VC_V; @@ -167,91 +162,121 @@ void ftime(char *timestr, bool withMS, int rms) int timespecToMS(struct timespec t) { - int rms = t.tv_nsec / 1000000; - rms += t.tv_sec * 1000; - return rms; + return (t.tv_nsec / 1000000) + (t.tv_sec * 1000); } -void drawSegments() +void drawSegmentNames() { - char data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11]; - char segmentTime[11]; - char *zeroStr = "-"; - char deltaTime[11]; - char sgmtTime[11]; - char segTime[11]; + char *names[segCount]; for(int i = 0; i < segCount; i++) { - ftime(segmentTime, false, pbrun[i].ms); - if (i >= currSeg) { - sprintf(data, "%10s%10s%10s%10s", zeroStr, zeroStr, zeroStr, segmentTime); - } else { - ftime(deltaTime, false, segments[i].ms - pbrun[i].ms); - ftime(sgmtTime, false, segments[i].ms - segments[i - 1].ms); - ftime(segTime, false, segments[i].ms); - if (segments[i].isSkipped) - sprintf(data, "%10s%10s%10s%10s", zeroStr, zeroStr, zeroStr, segmentTime); + names[i] = segments[i].name; + } + drawColumn(names, segCount, 0); +} + +//TODO: try to clean the branching up +void drawTimeColumn(int timeoption, int column) +{ + char *times[segCount]; + for (int i = 0; i < segCount; i++) { + times[i] = calloc(1, 11); + int time = 0; + switch (timeoption) { + case 0: + time = pbrun[i].ms; + break; + case 1: + if (i == currSeg) + time = currentMS - pbrun[i].ms; else - sprintf(data, "%10s%10s%10s%10s", deltaTime, sgmtTime, segTime, segmentTime); + time = segments[i].ms - pbrun[i].ms; + break; + case 2: + if (i > 0 && i < currSeg) + time = segments[i].ms - segments[i - 1].ms; + else if (i > 0 && i == currSeg) + time = currentMS - segments[i - 1].ms; + else if (i == 0 && i == currSeg) + time = currentMS; + else + time = segments[i].ms; + break; + case 3: + if (i == currSeg) + time = currentMS; + else + time = segments[i].ms; } - rghtPrint(6 + i, w, data); - leftPrint(6 + i, w, segments[i].name); + ftime(times[i], false, time); + } + drawColumn(times, segCount, column); + for (int i = 0; i < segCount; i++) { + free(times[i]); } } -void drawCurrentSegment() +void drawNotif(char* text) { - char data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11]; - strcpy(data, ""); - char pbTime[11]; - char deltaTime[11]; - char sgmtTime[11]; - char segTime[11]; - if (deltaOn) { - ftime(deltaTime, false, currentMS - pbrun[currSeg].ms); - strcat(data, deltaTime); - } - if (sgmtdurOn) { - if (currSeg == 0) - ftime(sgmtTime, false, currentMS); - else - ftime(sgmtTime, false, currentMS - segments[currSeg - 1].ms); - strcat(data, sgmtTime); - } - ftime(segTime, false, currentMS); - strcat(data, segTime); - if (pbOn) { - ftime(pbTime, true, pbrun[currSeg].ms); - strcat(data, pbTime); - } - data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11] = '\0'; - rghtPrint(6 + currSeg, w, data); - leftPrint(6 + currSeg, w, segments[currSeg].name); + clock_gettime(CLOCK_REALTIME, ¬if); + clearNotif(); + leftPrint(maxrows, w, text); +} + +void clearNotif() +{ + leftPrint(maxrows, w, "\033[2K"); +} + +void toggleCompact() +{ + compact = !compact; + //Clears the screen rather than dirtying it so the notif doesnt clear + clrScreen(); + if (compact) + drawNotif("Compact mode enabled"); + else + drawNotif("Compact mode disabled"); } void drawDisplay() { - if (resized) { + if (dirty) { clrScreen(); - drawSegments(); - resized = false; + dirty = false; } rghtPrint(1, w, "Attempts"); char atmpt[10]; sprintf(atmpt, "%9d", attempts); rghtPrint(2, w, atmpt); cntrPrint(1, w / 2, w, gameTitle); - cntrPrint(2, w / 2, w, categoryTitle); - char cols[41]; - sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB"); - rghtPrint(4, w, cols); + cntrPrint(2, w / 2, w, categoryTitle); drawHLine(5, w); - printf("\033[5;3H[dsph]"); - if (timerActive) { - drawSegments(); - drawCurrentSegment(); - struct timespec delta; - sub_timespec(timestart, finish, &delta); - currentMS = timespecToMS(delta); + printf("\033[5;3H"); + if (hotkeys_enabled || compact) + printf("["); + if (hotkeys_enabled) + printf("h"); + if (compact) + printf("c"); + if (hotkeys_enabled || compact) + printf("]"); + drawSegmentNames(); + //TODO: The column names stuff has to be more dynamic, part of the + //drawColumn function probably + if (!compact) { + char cols[41]; + sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB"); + rghtPrint(4, w, cols); + drawTimeColumn(0, 1); + drawTimeColumn(3, 2); + drawTimeColumn(2, 3); + drawTimeColumn(1, 4); + } else { + char cols[21]; + sprintf(cols, "%10s%10s", "Delta", "Time/PB"); + rghtPrint(4, w, cols); + drawTimeColumn(3, 1); + drawTimeColumn(1, 2); } drawHLine(segCount + 6, w); ftime(currentTime, true, currentMS); @@ -267,24 +292,22 @@ void resize(int i) h = ws.ws_row; setMaxCols(w); setMaxRows(h); - resized = true; + dirty = true; } -//This function will find an invalid run if theres only one in the history -//and that run is invalid. Also, runs with skipped segments aren't considered -//valid but should be void calculatePB() { - if (attempts == 0) - return; + bool valid = false; int bestMS = INT_MAX; int bestAttempt = 0; + + if (attempts == 0) + return; for (int i = 0; i < attempts; i++) { int run = i * segCount; - bool valid = true; + valid = true; for (int j = 0; j < segCount; j++) { - if (pastRuns[run + j].isSkipped == true || - pastRuns[run + j].isReset == true) + if (pastRuns[run + j].isReset == true) valid = false; } if (valid && pastRuns[run + segCount - 1].ms < bestMS) { @@ -292,8 +315,23 @@ void calculatePB() bestMS = pastRuns[run + segCount - 1].ms; } } + if (valid) + for (int i = 0; i < segCount; i++) + pbrun[i].ms = pastRuns[(bestAttempt * segCount) + i].ms; +} + +void calculateBestSegs() +{ + if (attempts == 0) + return; for (int i = 0; i < segCount; i++) { - pbrun[i].ms = pastRuns[(bestAttempt * segCount) + i].ms; + int bms = INT_MAX; + for (int j = 0; j < attempts; j++) { + int cms = pastRuns[(j * segCount) + i].ms; + if (cms != 0 && cms < bms) + bms = cms; + } + bestsegs[i].ms = bms; } } @@ -419,6 +457,7 @@ void loadFile() } cJSON_Delete(splitfile); calculatePB(); + calculateBestSegs(); } //Imports game/catagory names and segment names @@ -560,10 +599,16 @@ int main(int argc, char **argv) initScreen(bg, fg); loadFile(); while(!handleInput()) { - drawDisplay(); + struct timespec delta; + clock_gettime(CLOCK_REALTIME, &finish); + sub_timespec(notif, finish, &delta); + if (delta.tv_sec == 3) + clearNotif(); if (timerActive) { - clock_gettime(CLOCK_REALTIME, &finish); + sub_timespec(timestart, finish, &delta); + currentMS = timespecToMS(delta); } + drawDisplay(); usleep(4000); } resetScreen(); diff --git a/src/timer.h b/src/timer.h index f1d68f5..0cc7e80 100644 --- a/src/timer.h +++ b/src/timer.h @@ -18,7 +18,7 @@ #include #include "cJSON.h" -#define NS_PER_S 1000000000 +#define NS_PER_S 1000000000 #define cJSON_GetItem(x, y) cJSON_GetObjectItemCaseSensitive(x, y) struct segment @@ -47,8 +47,11 @@ void skip(); void loadKeymap(); void ftime(char *timestr, bool withMS, int ms); int timespecToMS(struct timespec t); -void drawSegments(); -void drawCurrentSegment(); +void drawNotif(); +void clearNotif(); +void drawSegmentNames(); +void drawTimeColumn(); +void toggleCompact(); void drawDisplay(); void resize(int i); void importSplitsIO(cJSON *splitfile);