quest/src/timer.c

576 lines
14 KiB
C
Raw Normal View History

2021-09-03 13:41:23 +10:00
#include "timer.h"
2021-08-18 16:56:42 +10:00
2021-09-05 02:50:19 +10:00
//Timekeeping
struct timespec timestart, finish;
int currentMS = 0;
bool timerActive;
//UI
2021-09-17 09:25:36 +10:00
struct color bg = { 47, 53, 66};
struct color fg = {247, 248, 242};
2021-08-28 01:43:33 +10:00
int h, w;
2021-09-17 09:25:36 +10:00
int deltaOn = 1;
int sgmtdurOn = 1;
int pbOn = 1;
bool resized = false;
2021-09-05 02:50:19 +10:00
2021-09-17 05:21:45 +10:00
//Splits.io data
2021-09-05 02:50:19 +10:00
const char *schemaver = "v1.0.1";
const char *timersname = "quest";
const char *timerlname = "Quinn's Utterly Elegant Speedrun Timer";
2021-09-17 05:21:45 +10:00
const char *timerver = "v0.5.0";
2021-09-05 02:50:19 +10:00
const char *timerlink = "https://github.com/SilentFungus/quest";
2021-09-17 05:21:45 +10:00
//Run data
char *filepath;
2021-09-17 09:25:36 +10:00
char *gameTitle = "title not loaded";
2021-09-03 13:41:23 +10:00
char *categoryTitle = "category not loaded";
2021-09-17 09:25:36 +10:00
int attempts = 0;
int bestTime = 0;
int bestAttempt = 0;
2021-09-03 13:41:23 +10:00
struct segment *segments;
2021-09-17 05:21:45 +10:00
struct segment *pbrun;
struct segment *wrrun;
struct segment *bestsegs;
2021-09-17 09:25:36 +10:00
struct pastseg *pastRuns;
int segCount;
int currSeg = -1;
2021-09-03 13:41:23 +10:00
char currentTime[10];
2021-08-18 16:56:42 +10:00
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td)
{
td->tv_nsec = t2.tv_nsec - t1.tv_nsec;
td->tv_sec = t2.tv_sec - t1.tv_sec;
if (td->tv_sec > 0 && td->tv_nsec < 0) {
td->tv_nsec += NS_PER_S;
td->tv_sec--;
} else if (td->tv_sec < 0 && td->tv_nsec > 0) {
td->tv_nsec -= NS_PER_S;
td->tv_sec++;
}
}
2021-08-28 01:43:33 +10:00
void start()
2021-08-18 16:56:42 +10:00
{
2021-09-17 09:25:36 +10:00
if (timerActive || segCount == 0)
2021-08-18 16:56:42 +10:00
return;
2021-08-28 01:43:33 +10:00
clock_gettime(CLOCK_REALTIME, &timestart);
2021-09-17 09:25:36 +10:00
timerActive = true;
resized = true;
currSeg = 0;
2021-08-18 16:56:42 +10:00
}
2021-08-28 01:43:33 +10:00
void stop()
2021-08-18 16:56:42 +10:00
{
if (!timerActive)
return;
if (currSeg < segCount)
segments[currSeg].isReset = true;
2021-08-18 16:56:42 +10:00
timerActive = false;
2021-09-17 05:21:45 +10:00
attempts++;
2021-09-17 09:25:36 +10:00
if (pastRuns)
pastRuns = realloc(pastRuns, attempts * segCount * sizeof(struct pastseg));
2021-09-17 05:21:45 +10:00
else
2021-09-17 09:25:36 +10:00
pastRuns = calloc(segCount, sizeof(struct pastseg));
for (int i = 0; i < segCount; i++) {
2021-09-17 05:21:45 +10:00
struct pastseg t;
2021-09-17 09:25:36 +10:00
t.ms = segments[i].ms;
2021-09-17 05:21:45 +10:00
t.isSkipped = segments[i].isSkipped;
t.isReset = segments[i].isReset;
2021-09-17 09:25:36 +10:00
pastRuns[((attempts-1) * segCount) + i] = t;
2021-09-17 05:21:45 +10:00
}
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;
2021-08-18 16:56:42 +10:00
}
2021-08-28 01:43:33 +10:00
void split()
{
2021-09-05 02:50:19 +10:00
if (!timerActive)
return;
2021-09-17 09:25:36 +10:00
segments[currSeg].ms = currentMS;
currSeg++;
if (currSeg >= segCount)
2021-09-05 02:50:19 +10:00
stop();
2021-09-17 09:25:36 +10:00
}
void unsplit()
{
if (!timerActive)
return;
currSeg--;
2021-08-28 01:43:33 +10:00
}
void tpause()
2021-08-18 16:56:42 +10:00
{
}
void skip()
{
if (!timerActive)
return;
segments[currSeg].isSkipped = true;
currSeg++;
if (currSeg >= segCount)
stop();
}
2021-08-18 16:56:42 +10:00
void loadKeymap()
{
km.START = VC_R;
km.STOP = VC_F;
km.PAUSE = VC_D;
km.SPLIT = VC_E;
2021-08-28 01:43:33 +10:00
km.CLOSE = VC_C;
2021-09-17 07:14:35 +10:00
km.HOTKS = VC_T;
2021-09-17 09:25:36 +10:00
km.USPLT = VC_G;
km.SKIP = VC_V;
2021-08-28 01:43:33 +10:00
}
2021-09-17 09:25:36 +10:00
void ftime(char *timestr, bool withMS, int rms)
2021-08-28 01:43:33 +10:00
{
2021-09-17 09:25:36 +10:00
int seconds = rms / 1000;
2021-09-05 02:50:19 +10:00
int minutes = seconds / 60;
int hours = minutes / 60;
//A few better formatted variables for displaying these numbers
2021-09-17 09:25:36 +10:00
int tms = (rms % 1000) / 10;
2021-09-05 02:50:19 +10:00
int oms = tms / 10;
int s = seconds % 60;
int m = minutes % 60;
int h = hours;
2021-08-28 01:43:33 +10:00
2021-09-05 02:50:19 +10:00
if (hours) {
if (withMS)
sprintf(timestr, fulltime, h, abs(h), abs(m), abs(s), abs(tms));
else
sprintf(timestr, hourstime, h, abs(m), abs(s));
} else if (minutes) {
if (withMS)
sprintf(timestr, sfulltime, m, abs(s), abs(tms));
else
sprintf(timestr, minutestime, m, abs(s));
} else {
if (withMS)
sprintf(timestr, secondstime, s, abs(tms));
else
sprintf(timestr, millitime, s, abs(oms));
}
2021-09-03 13:41:23 +10:00
}
int timespecToMS(struct timespec t)
{
2021-09-17 09:25:36 +10:00
int rms = t.tv_nsec / 1000000;
rms += t.tv_sec * 1000;
return rms;
2021-09-03 13:41:23 +10:00
}
void drawSegments()
{
2021-09-17 09:25:36 +10:00
char data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11];
2021-09-05 02:50:19 +10:00
char segmentTime[11];
2021-09-17 05:21:45 +10:00
char *zeroStr = "-";
2021-09-05 02:50:19 +10:00
char deltaTime[11];
char sgmtTime[11];
char segTime[11];
2021-09-17 09:25:36 +10:00
for(int i = 0; i < segCount; i++) {
ftime(segmentTime, false, pbrun[i].ms);
if (i >= currSeg) {
2021-09-03 13:41:23 +10:00
sprintf(data, "%10s%10s%10s%10s", zeroStr, zeroStr, zeroStr, segmentTime);
} else {
2021-09-17 09:25:36 +10:00
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);
2021-09-03 13:41:23 +10:00
}
rghtPrint(6 + i, w, data);
leftPrint(6 + i, w, segments[i].name);
}
2021-08-28 01:43:33 +10:00
}
2021-09-05 02:50:19 +10:00
void drawCurrentSegment()
{
2021-09-17 09:25:36 +10:00
char data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11];
2021-09-05 02:50:19 +10:00
strcpy(data, "");
char pbTime[11];
char deltaTime[11];
char sgmtTime[11];
char segTime[11];
2021-09-17 09:25:36 +10:00
if (deltaOn) {
ftime(deltaTime, false, currentMS - pbrun[currSeg].ms);
2021-09-05 02:50:19 +10:00
strcat(data, deltaTime);
}
2021-09-17 09:25:36 +10:00
if (sgmtdurOn) {
if (currSeg == 0)
2021-09-05 02:50:19 +10:00
ftime(sgmtTime, false, currentMS);
else
2021-09-17 09:25:36 +10:00
ftime(sgmtTime, false, currentMS - segments[currSeg - 1].ms);
2021-09-05 02:50:19 +10:00
strcat(data, sgmtTime);
}
ftime(segTime, false, currentMS);
strcat(data, segTime);
2021-09-17 09:25:36 +10:00
if (pbOn) {
ftime(pbTime, true, pbrun[currSeg].ms);
2021-09-05 02:50:19 +10:00
strcat(data, pbTime);
}
2021-09-17 09:25:36 +10:00
data[(deltaOn * 10) + (sgmtdurOn * 10) + (pbOn * 10) + 11] = '\0';
rghtPrint(6 + currSeg, w, data);
leftPrint(6 + currSeg, w, segments[currSeg].name);
2021-09-05 02:50:19 +10:00
}
2021-08-28 01:43:33 +10:00
void drawDisplay()
{
2021-09-05 02:50:19 +10:00
if (resized) {
clrScreen();
2021-09-17 05:21:45 +10:00
drawSegments();
2021-09-05 02:50:19 +10:00
resized = false;
}
2021-09-03 13:41:23 +10:00
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);
2021-08-28 01:43:33 +10:00
char cols[41];
sprintf(cols, "%10s%10s%10s%10s", "Delta", "Sgmt", "Time", "PB");
rghtPrint(4, w, cols);
2021-09-05 02:50:19 +10:00
drawHLine(5, w);
2021-09-17 05:21:45 +10:00
printf("\033[5;3H[dsph]");
2021-09-05 02:50:19 +10:00
if (timerActive) {
drawSegments();
2021-09-05 02:50:19 +10:00
drawCurrentSegment();
struct timespec delta;
sub_timespec(timestart, finish, &delta);
currentMS = timespecToMS(delta);
}
2021-09-17 09:25:36 +10:00
drawHLine(segCount + 6, w);
2021-09-05 02:50:19 +10:00
ftime(currentTime, true, currentMS);
2021-09-17 09:25:36 +10:00
rghtPrint(segCount + 7, w, currentTime);
2021-08-28 01:43:33 +10:00
fflush(stdout);
}
void resize(int i)
{
struct winsize ws;
ioctl(1, TIOCGWINSZ, &ws);
w = ws.ws_col;
h = ws.ws_row;
2021-09-17 05:21:45 +10:00
setMaxCols(w);
setMaxRows(h);
2021-09-05 02:50:19 +10:00
resized = true;
2021-08-18 16:56:42 +10:00
}
//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
2021-09-17 05:21:45 +10:00
void calculatePB()
2021-09-03 13:41:23 +10:00
{
2021-09-17 05:21:45 +10:00
if (attempts == 0)
return;
2021-09-17 09:25:36 +10:00
int bestMS = INT_MAX;
2021-09-17 05:21:45 +10:00
int bestAttempt = 0;
for (int i = 0; i < attempts; i++) {
2021-09-17 09:25:36 +10:00
int run = i * segCount;
2021-09-17 05:21:45 +10:00
bool valid = true;
2021-09-17 09:25:36 +10:00
for (int j = 0; j < segCount; j++) {
if (pastRuns[run + j].isSkipped == true ||
pastRuns[run + j].isReset == true)
2021-09-17 05:21:45 +10:00
valid = false;
}
2021-09-17 09:25:36 +10:00
if (valid && pastRuns[run + segCount - 1].ms < bestMS) {
2021-09-17 05:21:45 +10:00
bestAttempt = i;
2021-09-17 09:25:36 +10:00
bestMS = pastRuns[run + segCount - 1].ms;
2021-09-17 05:21:45 +10:00
}
}
2021-09-17 09:25:36 +10:00
for (int i = 0; i < segCount; i++) {
pbrun[i].ms = pastRuns[(bestAttempt * segCount) + i].ms;
2021-09-17 05:21:45 +10:00
}
}
//TODO: it'll be more efficent if all the segments pointers point at the same
//instance of the segments name, instead of copying the contents over
void loadFile()
{
//char path[256];
//strcat(strcpy(path, getenv("HOME")), "/.config/qtimer");
//mkdir(path, 0777);
//strcat(strcpy(path, getenv("HOME")), "/.config/qtimer/keymaps");
//mkdir(path, 0777);
//strcat(strcpy(path, getenv("HOME")), "/.config/qtimer/keymaps/default");
//FILE* fp = fopen(path, "r");
// READING THE FILE TO A BUFFER
//fclose(fp);
2021-09-03 13:41:23 +10:00
char *buffer = NULL;
long length;
2021-09-17 05:21:45 +10:00
FILE *f = fopen(filepath, "rb");
2021-09-03 13:41:23 +10:00
if (f == NULL)
return;
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
buffer = malloc(length + 1);
2021-09-17 05:21:45 +10:00
if (buffer != NULL)
2021-09-03 13:41:23 +10:00
fread(buffer, 1, length, f);
fclose(f);
buffer[length] = '\0';
2021-09-17 05:21:45 +10:00
2021-09-03 13:41:23 +10:00
cJSON *splitfile = cJSON_Parse(buffer);
2021-09-17 05:21:45 +10:00
free(buffer);
2021-09-17 09:25:36 +10:00
cJSON *schema = cJSON_GetItem(splitfile, "_schemaVersion");
2021-09-17 05:21:45 +10:00
if (schema) {
importSplitsIO(splitfile);
return;
}
2021-09-03 13:41:23 +10:00
cJSON *game = NULL;
2021-09-17 05:21:45 +10:00
cJSON *cate = NULL;
cJSON *atts = NULL;
2021-09-03 13:41:23 +10:00
cJSON *segs = NULL;
2021-09-17 05:21:45 +10:00
cJSON *runs = NULL;
2021-09-17 09:25:36 +10:00
game = cJSON_GetItem(splitfile, "game");
cate = cJSON_GetItem(splitfile, "category");
atts = cJSON_GetItem(splitfile, "attempts");
segs = cJSON_GetItem(splitfile, "segments");
runs = cJSON_GetItem(splitfile, "history");
2021-09-17 05:21:45 +10:00
2021-09-03 13:41:23 +10:00
if (game) {
2021-09-17 09:25:36 +10:00
cJSON *title = cJSON_GetItem(game, "name");
2021-09-03 13:41:23 +10:00
if (cJSON_IsString(title) && (title->valuestring != NULL)) {
gameTitle = malloc(strlen(title->valuestring));
strcpy(gameTitle, title->valuestring);
}
}
2021-09-17 05:21:45 +10:00
if (cate) {
2021-09-17 09:25:36 +10:00
cJSON *title = cJSON_GetItem(cate, "name");
2021-09-03 13:41:23 +10:00
if (cJSON_IsString(title) && (title->valuestring != NULL)) {
categoryTitle = malloc(strlen(title->valuestring));
strcpy(categoryTitle, title->valuestring);
}
}
2021-09-17 05:21:45 +10:00
if (atts) {
2021-09-17 09:25:36 +10:00
cJSON *total = cJSON_GetItem(atts, "total");
2021-09-03 13:41:23 +10:00
if (cJSON_IsNumber(total))
attempts = total->valueint;
}
if (segs) {
2021-09-17 09:25:36 +10:00
segCount = cJSON_GetArraySize(segs);
segments = calloc(segCount, sizeof(struct segment));
pbrun = calloc(segCount, sizeof(struct segment));
wrrun = calloc(segCount, sizeof(struct segment));
bestsegs = calloc(segCount, sizeof(struct segment));
2021-09-03 13:41:23 +10:00
int it = 0;
cJSON *iterator = NULL;
cJSON *segname = NULL;
cJSON_ArrayForEach(iterator, segs) {
2021-09-17 09:25:36 +10:00
segname = cJSON_GetItem(iterator, "name");
2021-09-03 13:41:23 +10:00
if (cJSON_IsString(segname) && (segname->valuestring != NULL)) {
segments[it].name = malloc(strlen(segname->valuestring));
strcpy(segments[it].name, segname->valuestring);
}
2021-09-17 05:21:45 +10:00
it++;
}
}
if (runs) {
2021-09-17 09:25:36 +10:00
pastRuns = calloc(cJSON_GetArraySize(runs) * segCount, sizeof(struct pastseg));
2021-09-17 05:21:45 +10:00
int oI = 0;
cJSON *oIterator = NULL;
cJSON_ArrayForEach(oIterator, runs) {
int iI = 0;
cJSON *iIterator = NULL;
cJSON_ArrayForEach(iIterator, oIterator) {
struct pastseg t;
2021-09-17 09:25:36 +10:00
cJSON *rms = cJSON_GetItem(iIterator, "rms");
cJSON *skp = cJSON_GetItem(iIterator, "skipped");
cJSON *rst = cJSON_GetItem(iIterator, "reset");
2021-09-17 05:21:45 +10:00
2021-09-17 09:25:36 +10:00
t.ms = rms->valueint;
2021-09-17 05:21:45 +10:00
if (cJSON_IsTrue(skp))
t.isSkipped = true;
else
t.isSkipped = false;
if (cJSON_IsTrue(rst))
t.isReset = true;
else
t.isReset = false;
2021-09-17 09:25:36 +10:00
pastRuns[(oI * segCount) + iI] = t;
2021-09-17 05:21:45 +10:00
iI++;
}
oI++;
}
}
cJSON_Delete(splitfile);
calculatePB();
}
//Imports game/catagory names and segment names
void importSplitsIO(cJSON *splitfile)
{
cJSON *game = NULL;
cJSON *cate = NULL;
cJSON *segs = NULL;
2021-09-17 09:25:36 +10:00
game = cJSON_GetItem(splitfile, "game");
cate = cJSON_GetItem(splitfile, "category");
segs = cJSON_GetItem(splitfile, "segments");
2021-09-17 05:21:45 +10:00
if (game) {
2021-09-17 09:25:36 +10:00
cJSON *title = cJSON_GetItem(game, "longname");
2021-09-17 05:21:45 +10:00
if (cJSON_IsString(title) && (title->valuestring != NULL)) {
gameTitle = malloc(strlen(title->valuestring));
strcpy(gameTitle, title->valuestring);
}
}
if (cate) {
2021-09-17 09:25:36 +10:00
cJSON *title = cJSON_GetItem(cate, "longname");
2021-09-17 05:21:45 +10:00
if (cJSON_IsString(title) && (title->valuestring != NULL)) {
categoryTitle = malloc(strlen(title->valuestring));
strcpy(categoryTitle, title->valuestring);
}
}
if (segs) {
2021-09-17 09:25:36 +10:00
segCount = cJSON_GetArraySize(segs);
segments = calloc(segCount, sizeof(struct segment));
pbrun = calloc(segCount, sizeof(struct segment));
wrrun = calloc(segCount, sizeof(struct segment));
bestsegs = calloc(segCount, sizeof(struct segment));
2021-09-17 05:21:45 +10:00
cJSON *segname = NULL;
int it = 0;
cJSON *iterator = NULL;
cJSON_ArrayForEach(iterator, segs) {
2021-09-17 09:25:36 +10:00
segname = cJSON_GetItem(iterator, "name");
2021-09-17 05:21:45 +10:00
if (cJSON_IsString(segname) && (segname->valuestring != NULL)) {
segments[it].name = malloc(strlen(segname->valuestring));
strcpy(segments[it].name, segname->valuestring);
2021-09-03 13:41:23 +10:00
}
it++;
}
}
cJSON_Delete(splitfile);
}
2021-09-17 05:21:45 +10:00
void saveFile()
{
if (timerActive)
return;
cJSON *splitfile = cJSON_CreateObject();
cJSON *game = cJSON_CreateObject();
cJSON *cate = cJSON_CreateObject();
cJSON *atts = cJSON_CreateObject();
cJSON *segs = cJSON_CreateArray();
cJSON *runs = cJSON_CreateArray();
cJSON *gameName = cJSON_CreateString(gameTitle);
cJSON *cateName = cJSON_CreateString(categoryTitle);
cJSON *attCount = cJSON_CreateNumber(attempts);
cJSON_AddItemToObject(game, "name", gameName);
cJSON_AddItemToObject(cate, "name", cateName);
cJSON_AddItemToObject(atts, "total", attCount);
cJSON_AddItemToObject(splitfile, "game", game);
cJSON_AddItemToObject(splitfile, "category", cate);
cJSON_AddItemToObject(splitfile, "attempts", atts);
2021-09-17 09:25:36 +10:00
for(int i = 0; i < segCount; i++) {
2021-09-17 05:21:45 +10:00
cJSON *seg = cJSON_CreateObject();
cJSON *segn = cJSON_CreateString(segments[i].name);
cJSON_AddItemToObject(seg, "name", segn);
cJSON_AddItemToArray(segs, seg);
}
cJSON_AddItemToObject(splitfile, "segments", segs);
for (int i = 0; i < attempts; i++) {
cJSON *run = cJSON_CreateArray();
2021-09-17 09:25:36 +10:00
for (int j = 0; j < segCount; j++) {
struct pastseg t = pastRuns[(i * segCount) + j];
2021-09-17 05:21:45 +10:00
cJSON *seg = cJSON_CreateObject();
2021-09-17 09:25:36 +10:00
cJSON *tim = cJSON_CreateNumber(t.ms);
2021-09-17 05:21:45 +10:00
cJSON *skp = cJSON_CreateBool(t.isSkipped);
cJSON *rst = cJSON_CreateBool(t.isReset);
2021-09-17 09:25:36 +10:00
cJSON_AddItemToObject(seg, "rms", tim);
2021-09-17 05:21:45 +10:00
cJSON_AddItemToObject(seg, "skipped", skp);
cJSON_AddItemToObject(seg, "reset", rst);
cJSON_AddItemToArray(run, seg);
}
cJSON_AddItemToArray(runs, run);
}
cJSON_AddItemToObject(splitfile, "history", runs);
char *string = cJSON_Print(splitfile);
if (string != NULL) {
FILE *f = fopen(filepath, "w");
if (f == NULL)
return;
fwrite(string, 1, strlen(string), f);
fclose(f);
}
cJSON_Delete(splitfile);
}
2021-08-28 01:43:33 +10:00
int main(int argc, char **argv)
2021-08-18 16:56:42 +10:00
{
timerActive = false;
2021-09-17 05:21:45 +10:00
filepath = argv[1];
2021-08-18 16:56:42 +10:00
hook_set_logger_proc(&logger_proc);
hook_set_dispatch_proc(&dispatch_proc);
//IPC pipe
pid_t cpid;
pipe(pipefd);
fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
loadKeymap();
cpid = fork();
2021-09-17 05:21:45 +10:00
2021-08-18 16:56:42 +10:00
if (cpid == 0) {
close(pipefd[0]);
hook_run();
} else {
close(pipefd[1]);
2021-08-28 01:43:33 +10:00
signal(SIGWINCH, resize);
resize(0);
initScreen(bg, fg);
2021-09-17 05:21:45 +10:00
loadFile();
2021-08-28 01:43:33 +10:00
while(!handleInput()) {
drawDisplay();
2021-08-18 16:56:42 +10:00
if (timerActive) {
clock_gettime(CLOCK_REALTIME, &finish);
}
2021-09-17 05:21:45 +10:00
usleep(4000);
2021-08-18 16:56:42 +10:00
}
2021-08-28 01:43:33 +10:00
resetScreen();
kill(cpid, SIGTERM);
2021-08-18 16:56:42 +10:00
}
2021-09-17 05:21:45 +10:00
2021-08-11 23:50:59 +10:00
return 0;
}
2021-09-03 13:41:23 +10:00