added non global hotkeys and new display features

This commit is contained in:
Lexi Quinn 2021-09-29 05:15:13 +10:00
parent 8e3683340d
commit 24b576f59f
7 changed files with 177 additions and 114 deletions

View File

@ -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. splits after the first use of the split file is currently unsupported.
### Default Keybinds ### Default Keybinds
| Keys | Action | | Keys | Action | Global |
| ---- | --------------------- | | ---- | --------------------- | ------ |
| `R` | Start | | `R` | Start | YES |
| `F` | Stop / Reset | | `F` | Stop / Reset | YES |
| `E` | Split | | `E` | Split | YES |
| `G` | Undo split | | `G` | Undo split | YES |
| `V` | Skip split | | `V` | Skip split | YES |
| `C` | Close | | `Q` | Close | NO |
| `T` | Toggle global hotkeys | | `T` | Toggle global hotkeys | YES |
| `C` | Toggle compact UI | NO |
Customisable hotkeys without editing the source code coming soon! Customisable hotkeys without editing the source code coming soon!

View File

@ -10,6 +10,7 @@ const char *sfulltime = "%4d:%02d.%02d";
int maxrows = INT_MAX; int maxrows = INT_MAX;
int maxcols = INT_MAX; int maxcols = INT_MAX;
int colwidth = 10; int colwidth = 10;
int in = 0;
struct termios base; struct termios base;
@ -55,6 +56,8 @@ void initScreen(struct color bg, struct color fg)
t = base; t = base;
t.c_lflag &= (~ECHO & ~ICANON); t.c_lflag &= (~ECHO & ~ICANON);
tcsetattr(1, TCSANOW, &t); tcsetattr(1, TCSANOW, &t);
dup(0);
fcntl(0, F_SETFL, O_NONBLOCK);
altBuffer(); altBuffer();
setBGColor(bg); setBGColor(bg);
setFGColor(fg); setFGColor(fg);
@ -99,9 +102,9 @@ void drawHLine(int row, int maxlen)
//from the right hand side towards the left. //from the right hand side towards the left.
void drawColumn(char **data, int count, int column) void drawColumn(char **data, int count, int column)
{ {
int x = maxcols - (column * 10); int x = maxcols - (column * 10) + 1;
int y = 6; int y = 6;
if (column == 1) if (column == 0)
x = 1; x = 1;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
printf(MVCUR, y + i, x, data[i]); printf(MVCUR, y + i, x, data[i]);

View File

@ -5,6 +5,8 @@
#include <string.h> #include <string.h>
#include <termios.h> #include <termios.h>
#include <limits.h> #include <limits.h>
#include <fcntl.h>
#include <unistd.h>
extern const char *millitime; extern const char *millitime;
extern const char *secondstime; extern const char *secondstime;
@ -15,6 +17,7 @@ extern const char *sfulltime;
extern int maxrows; extern int maxrows;
extern int maxcols; extern int maxcols;
extern int colwidth; extern int colwidth;
extern int in;
struct color { struct color {
int r; int r;

View File

@ -21,8 +21,6 @@ void dispatch_proc(uiohook_event * const event) {
buf = K_PAUSE; buf = K_PAUSE;
if (event->data.keyboard.keycode == km.SPLIT) if (event->data.keyboard.keycode == km.SPLIT)
buf = K_SPLIT; buf = K_SPLIT;
if (event->data.keyboard.keycode == km.CLOSE)
buf = K_CLOSE;
if (event->data.keyboard.keycode == km.HOTKS) if (event->data.keyboard.keycode == km.HOTKS)
buf = K_HOTKS; buf = K_HOTKS;
if (event->data.keyboard.keycode == km.USPLT) if (event->data.keyboard.keycode == km.USPLT)
@ -38,6 +36,8 @@ void dispatch_proc(uiohook_event * const event) {
int handleInput() int handleInput()
{ {
ssize_t rd = read(pipefd[0], &buf, 1); ssize_t rd = read(pipefd[0], &buf, 1);
char t;
read(in, &t, 1);
if ((!hotkeys_enabled && buf != K_HOTKS) || rd == -1) if ((!hotkeys_enabled && buf != K_HOTKS) || rd == -1)
return 0; return 0;
if (buf == K_SPLIT) if (buf == K_SPLIT)
@ -48,13 +48,20 @@ int handleInput()
stop(); stop();
if (buf == K_PAUSE) if (buf == K_PAUSE)
tpause(); tpause();
if (buf == K_HOTKS) if (buf == K_HOTKS) {
hotkeys_enabled = !hotkeys_enabled; hotkeys_enabled = !hotkeys_enabled;
if (hotkeys_enabled)
drawNotif("Global hotkeys enabled");
else
drawNotif("Global hotkeys disabled");
}
if (buf == K_USPLT) if (buf == K_USPLT)
unsplit(); unsplit();
if (buf == K_SKIP) if (buf == K_SKIP)
skip(); skip();
if (buf == K_CLOSE) if (t == 'c')
toggleCompact();
if (t == 'q')
return 1; return 1;
return 0; return 0;
} }

View File

@ -4,15 +4,17 @@
#include <uiohook.h> #include <uiohook.h>
#include <unistd.h> #include <unistd.h>
#include "timer.h" #include "timer.h"
#include "display.h"
#define K_START 1 #define K_START 1
#define K_STOP 2 #define K_STOP 2
#define K_PAUSE 3 #define K_PAUSE 3
#define K_SPLIT 4 #define K_SPLIT 4
#define K_CLOSE 5 #define K_HOTKS 5
#define K_HOTKS 6 #define K_USPLT 6
#define K_USPLT 7 #define K_SKIP 7
#define K_SKIP 8
extern bool hotkeys_enabled;
struct keymap struct keymap
{ {
@ -20,7 +22,6 @@ struct keymap
uint16_t STOP; uint16_t STOP;
uint16_t PAUSE; uint16_t PAUSE;
uint16_t SPLIT; uint16_t SPLIT;
uint16_t CLOSE;
uint16_t HOTKS; uint16_t HOTKS;
uint16_t USPLT; uint16_t USPLT;
uint16_t SKIP; uint16_t SKIP;

View File

@ -1,7 +1,7 @@
#include "timer.h" #include "timer.h"
//Timekeeping //Timekeeping
struct timespec timestart, finish; struct timespec timestart, finish, notif;
int currentMS = 0; int currentMS = 0;
bool timerActive; bool timerActive;
@ -9,10 +9,8 @@ bool timerActive;
struct color bg = { 47, 53, 66}; struct color bg = { 47, 53, 66};
struct color fg = {247, 248, 242}; struct color fg = {247, 248, 242};
int h, w; int h, w;
int deltaOn = 1; bool compact = false;
int sgmtdurOn = 1; bool dirty = false;
int pbOn = 1;
bool resized = false;
//Splits.io data //Splits.io data
const char *schemaver = "v1.0.1"; const char *schemaver = "v1.0.1";
@ -56,7 +54,12 @@ void start()
return; return;
clock_gettime(CLOCK_REALTIME, &timestart); clock_gettime(CLOCK_REALTIME, &timestart);
timerActive = true; 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; currSeg = 0;
} }
@ -81,14 +84,6 @@ void stop()
} }
calculatePB(); calculatePB();
saveFile(); 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() void split()
@ -117,6 +112,7 @@ void skip()
{ {
if (!timerActive) if (!timerActive)
return; return;
if (currSeg < segCount)
segments[currSeg].isSkipped = true; segments[currSeg].isSkipped = true;
currSeg++; currSeg++;
if (currSeg >= segCount) if (currSeg >= segCount)
@ -129,7 +125,6 @@ void loadKeymap()
km.STOP = VC_F; km.STOP = VC_F;
km.PAUSE = VC_D; km.PAUSE = VC_D;
km.SPLIT = VC_E; km.SPLIT = VC_E;
km.CLOSE = VC_C;
km.HOTKS = VC_T; km.HOTKS = VC_T;
km.USPLT = VC_G; km.USPLT = VC_G;
km.SKIP = VC_V; km.SKIP = VC_V;
@ -167,73 +162,87 @@ void ftime(char *timestr, bool withMS, int rms)
int timespecToMS(struct timespec t) int timespecToMS(struct timespec t)
{ {
int rms = t.tv_nsec / 1000000; return (t.tv_nsec / 1000000) + (t.tv_sec * 1000);
rms += t.tv_sec * 1000;
return rms;
} }
void drawSegments() void drawSegmentNames()
{ {
char data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11]; char *names[segCount];
char segmentTime[11];
char *zeroStr = "-";
char deltaTime[11];
char sgmtTime[11];
char segTime[11];
for(int i = 0; i < segCount; i++) { for(int i = 0; i < segCount; i++) {
ftime(segmentTime, false, pbrun[i].ms); names[i] = segments[i].name;
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);
else
sprintf(data, "%10s%10s%10s%10s", deltaTime, sgmtTime, segTime, segmentTime);
} }
rghtPrint(6 + i, w, data); drawColumn(names, segCount, 0);
leftPrint(6 + i, w, segments[i].name); }
//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
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;
}
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]; clock_gettime(CLOCK_REALTIME, &notif);
strcpy(data, ""); clearNotif();
char pbTime[11]; leftPrint(maxrows, w, text);
char deltaTime[11]; }
char sgmtTime[11];
char segTime[11]; void clearNotif()
if (deltaOn) { {
ftime(deltaTime, false, currentMS - pbrun[currSeg].ms); leftPrint(maxrows, w, "\033[2K");
strcat(data, deltaTime); }
}
if (sgmtdurOn) { void toggleCompact()
if (currSeg == 0) {
ftime(sgmtTime, false, currentMS); compact = !compact;
//Clears the screen rather than dirtying it so the notif doesnt clear
clrScreen();
if (compact)
drawNotif("Compact mode enabled");
else else
ftime(sgmtTime, false, currentMS - segments[currSeg - 1].ms); drawNotif("Compact mode disabled");
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);
} }
void drawDisplay() void drawDisplay()
{ {
if (resized) { if (dirty) {
clrScreen(); clrScreen();
drawSegments(); dirty = false;
resized = false;
} }
rghtPrint(1, w, "Attempts"); rghtPrint(1, w, "Attempts");
char atmpt[10]; char atmpt[10];
@ -241,17 +250,33 @@ void drawDisplay()
rghtPrint(2, w, atmpt); rghtPrint(2, w, atmpt);
cntrPrint(1, w / 2, w, gameTitle); cntrPrint(1, w / 2, w, gameTitle);
cntrPrint(2, w / 2, w, categoryTitle); cntrPrint(2, w / 2, w, categoryTitle);
drawHLine(5, w);
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]; char cols[41];
sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB"); sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB");
rghtPrint(4, w, cols); rghtPrint(4, w, cols);
drawHLine(5, w); drawTimeColumn(0, 1);
printf("\033[5;3H[dsph]"); drawTimeColumn(3, 2);
if (timerActive) { drawTimeColumn(2, 3);
drawSegments(); drawTimeColumn(1, 4);
drawCurrentSegment(); } else {
struct timespec delta; char cols[21];
sub_timespec(timestart, finish, &delta); sprintf(cols, "%10s%10s", "Delta", "Time/PB");
currentMS = timespecToMS(delta); rghtPrint(4, w, cols);
drawTimeColumn(3, 1);
drawTimeColumn(1, 2);
} }
drawHLine(segCount + 6, w); drawHLine(segCount + 6, w);
ftime(currentTime, true, currentMS); ftime(currentTime, true, currentMS);
@ -267,24 +292,22 @@ void resize(int i)
h = ws.ws_row; h = ws.ws_row;
setMaxCols(w); setMaxCols(w);
setMaxRows(h); 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() void calculatePB()
{ {
if (attempts == 0) bool valid = false;
return;
int bestMS = INT_MAX; int bestMS = INT_MAX;
int bestAttempt = 0; int bestAttempt = 0;
if (attempts == 0)
return;
for (int i = 0; i < attempts; i++) { for (int i = 0; i < attempts; i++) {
int run = i * segCount; int run = i * segCount;
bool valid = true; valid = true;
for (int j = 0; j < segCount; j++) { for (int j = 0; j < segCount; j++) {
if (pastRuns[run + j].isSkipped == true || if (pastRuns[run + j].isReset == true)
pastRuns[run + j].isReset == true)
valid = false; valid = false;
} }
if (valid && pastRuns[run + segCount - 1].ms < bestMS) { if (valid && pastRuns[run + segCount - 1].ms < bestMS) {
@ -292,8 +315,23 @@ void calculatePB()
bestMS = pastRuns[run + segCount - 1].ms; bestMS = pastRuns[run + segCount - 1].ms;
} }
} }
for (int i = 0; i < segCount; i++) { if (valid)
for (int i = 0; i < segCount; i++)
pbrun[i].ms = pastRuns[(bestAttempt * segCount) + i].ms; pbrun[i].ms = pastRuns[(bestAttempt * segCount) + i].ms;
}
void calculateBestSegs()
{
if (attempts == 0)
return;
for (int i = 0; i < segCount; i++) {
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); cJSON_Delete(splitfile);
calculatePB(); calculatePB();
calculateBestSegs();
} }
//Imports game/catagory names and segment names //Imports game/catagory names and segment names
@ -560,10 +599,16 @@ int main(int argc, char **argv)
initScreen(bg, fg); initScreen(bg, fg);
loadFile(); loadFile();
while(!handleInput()) { while(!handleInput()) {
drawDisplay(); struct timespec delta;
if (timerActive) {
clock_gettime(CLOCK_REALTIME, &finish); clock_gettime(CLOCK_REALTIME, &finish);
sub_timespec(notif, finish, &delta);
if (delta.tv_sec == 3)
clearNotif();
if (timerActive) {
sub_timespec(timestart, finish, &delta);
currentMS = timespecToMS(delta);
} }
drawDisplay();
usleep(4000); usleep(4000);
} }
resetScreen(); resetScreen();

View File

@ -47,8 +47,11 @@ void skip();
void loadKeymap(); void loadKeymap();
void ftime(char *timestr, bool withMS, int ms); void ftime(char *timestr, bool withMS, int ms);
int timespecToMS(struct timespec t); int timespecToMS(struct timespec t);
void drawSegments(); void drawNotif();
void drawCurrentSegment(); void clearNotif();
void drawSegmentNames();
void drawTimeColumn();
void toggleCompact();
void drawDisplay(); void drawDisplay();
void resize(int i); void resize(int i);
void importSplitsIO(cJSON *splitfile); void importSplitsIO(cJSON *splitfile);