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.
### 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!

View File

@ -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]);

View File

@ -5,6 +5,8 @@
#include <string.h>
#include <termios.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
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;

View File

@ -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;
}

View File

@ -4,15 +4,17 @@
#include <uiohook.h>
#include <unistd.h>
#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;

View File

@ -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, &timestart);
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,6 +112,7 @@ void skip()
{
if (!timerActive)
return;
if (currSeg < segCount)
segments[currSeg].isSkipped = true;
currSeg++;
if (currSeg >= segCount)
@ -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,73 +162,87 @@ 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);
else
sprintf(data, "%10s%10s%10s%10s", deltaTime, sgmtTime, segTime, segmentTime);
names[i] = segments[i].name;
}
rghtPrint(6 + i, w, data);
leftPrint(6 + i, w, 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
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];
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);
clock_gettime(CLOCK_REALTIME, &notif);
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
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);
drawNotif("Compact mode disabled");
}
void drawDisplay()
{
if (resized) {
if (dirty) {
clrScreen();
drawSegments();
resized = false;
dirty = false;
}
rghtPrint(1, w, "Attempts");
char atmpt[10];
@ -241,17 +250,33 @@ void drawDisplay()
rghtPrint(2, w, atmpt);
cntrPrint(1, w / 2, w, gameTitle);
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];
sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB");
rghtPrint(4, w, cols);
drawHLine(5, w);
printf("\033[5;3H[dsph]");
if (timerActive) {
drawSegments();
drawCurrentSegment();
struct timespec delta;
sub_timespec(timestart, finish, &delta);
currentMS = timespecToMS(delta);
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;
}
}
for (int i = 0; i < segCount; i++) {
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++) {
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();
if (timerActive) {
struct timespec delta;
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);
}
resetScreen();

View File

@ -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);