Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
b700113b88 | |||
dbf8dce580 | |||
9c0d4befba | |||
d09b991771 | |||
057d051a3a | |||
073e610966 | |||
47369a86e9 | |||
0812d68dd1 | |||
f1e2c85e3a | |||
1bb5864a29 | |||
fd8bbadb1e | |||
b93f2f0aa7 | |||
c24f516380 | |||
1aa1ed514a | |||
c7387f5acb | |||
d3f7a25a64 | |||
f5e5c506af | |||
b208382e60 | |||
9c7ccfe0b0 | |||
65414c2d64 |
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
|||||||
TARGET = quest
|
TARGET = quest
|
||||||
LIBS = -lm -luiohook -lcjson
|
LIBS = -lm -luiohook -lcjson -lxcb -lXinerama -lX11
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -g -Wall
|
CFLAGS = -g -Wall
|
||||||
INSTALL_PATH = /usr/local
|
INSTALL_PATH = /usr/local
|
||||||
|
2
default.nix
Normal file
2
default.nix
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.callPackage ./derivation.nix {}
|
23
derivation.nix
Normal file
23
derivation.nix
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{ stdenv }:
|
||||||
|
stdenv.mkDerivation rec {
|
||||||
|
name = "quest-${version}";
|
||||||
|
version = "0.7";
|
||||||
|
|
||||||
|
src = ./src2/.;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ ];
|
||||||
|
buildInputs = [ ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
gcc server.c -o quest-daemon
|
||||||
|
gcc client.c -o quest-log
|
||||||
|
gcc tui.c -o quest
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp quest-daemon $out/bin
|
||||||
|
cp quest-log $out/bin
|
||||||
|
cp quest $out/bin
|
||||||
|
'';
|
||||||
|
}
|
187
formatspec.md
Normal file
187
formatspec.md
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
Introduction
|
||||||
|
============
|
||||||
|
The quest file format is a declarative computer language. It is made up
|
||||||
|
of statements that when read by the software define how it should behave and
|
||||||
|
statements that are written by the software to declare what it recorded.
|
||||||
|
Each statement begins with a directive followed by options or arguments indented
|
||||||
|
by one tab on the following lines.
|
||||||
|
The order of the directives is important, they are read by the program
|
||||||
|
line by line and may inherit data from those placed above.
|
||||||
|
If a line does not begin with a recognizable directive, it is ignored.
|
||||||
|
Care should be taken when inserting comments as one program may recognize
|
||||||
|
directives that another does not, for this reason double backslashes (//) are
|
||||||
|
reserved for leaving comments
|
||||||
|
|
||||||
|
List of directives
|
||||||
|
==================
|
||||||
|
The indented section of the directives reference show example values first,
|
||||||
|
followed by the range of possible values or brief explaination in brackets
|
||||||
|
|
||||||
|
Option directives
|
||||||
|
-----------------
|
||||||
|
Any client is free to define their own directives to look for in this section.
|
||||||
|
These directives override a programs defaults, they should be placed at the
|
||||||
|
top of the file alongside MetaData directives
|
||||||
|
|
||||||
|
Foreground-Color
|
||||||
|
247, 248, 242 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Background-Color
|
||||||
|
47, 53, 66 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Best-Color
|
||||||
|
249, 255, 79 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Ahead-Gaining-Color
|
||||||
|
24, 240, 31 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Ahead-Losing-Color
|
||||||
|
79, 255, 85 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Behind-Gaining-Color
|
||||||
|
255, 79, 79 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Behind-Losing-Color
|
||||||
|
224, 34, 34 (0-255, 0-255, 0-255)
|
||||||
|
|
||||||
|
Comparison
|
||||||
|
Personal-Best (Personal-Best/World-Record/Sum-of-Best)
|
||||||
|
|
||||||
|
Show-Delta-Column
|
||||||
|
True (True/False)
|
||||||
|
|
||||||
|
Show-Segment-Column
|
||||||
|
True (True/False)
|
||||||
|
|
||||||
|
Show-Time-Column
|
||||||
|
True (True/False)
|
||||||
|
|
||||||
|
Show-Comparison-Column
|
||||||
|
True (True/False)
|
||||||
|
|
||||||
|
Toggle-Hotkeys
|
||||||
|
t (See key name section)
|
||||||
|
|
||||||
|
Start-Key
|
||||||
|
r (See key name section)
|
||||||
|
|
||||||
|
Stop-Key
|
||||||
|
f (See key name section)
|
||||||
|
|
||||||
|
Pause-Key
|
||||||
|
y (See key name section)
|
||||||
|
|
||||||
|
Split-Key
|
||||||
|
e (See key name section)
|
||||||
|
|
||||||
|
Unsplit-Key
|
||||||
|
g (See key name section)
|
||||||
|
|
||||||
|
Skip-Key
|
||||||
|
v (See key name section)
|
||||||
|
|
||||||
|
MetaData directives
|
||||||
|
---------------
|
||||||
|
These directives provide metadata to runs that follow them,
|
||||||
|
multiple of these can appear in a single file if multiple
|
||||||
|
games or categories are run after one another
|
||||||
|
|
||||||
|
Title
|
||||||
|
Elden Ring (Title of the game being run)
|
||||||
|
|
||||||
|
Category
|
||||||
|
Any% (Name of the category being run)
|
||||||
|
|
||||||
|
Runner
|
||||||
|
SuperCoolGuy04 (The name you as the runner are known by)
|
||||||
|
|
||||||
|
Segment and Route Directives
|
||||||
|
----------------------------
|
||||||
|
Runs by themselves are simply a list of events that occured
|
||||||
|
(such as splitting or pausing) in the quest language, in order to give meaning
|
||||||
|
to this data, these more complicated directives are used to define segments
|
||||||
|
that are played between splits and routes made up of these segments.
|
||||||
|
Define all your possible segments first, followed by all routes.
|
||||||
|
If no segments are defined, a single unnamed segment is to be assumed.
|
||||||
|
If no routes are defined, a single unnamed route that passes through all
|
||||||
|
segments in the order of their definition is to be assumed.
|
||||||
|
|
||||||
|
Segment
|
||||||
|
Shortname
|
||||||
|
Stage One (A name for the segment that should identify it
|
||||||
|
within the file and refer to the single
|
||||||
|
objective completed.
|
||||||
|
This name should not change.)
|
||||||
|
Longname
|
||||||
|
Murderize Chad (The display name for the segment if it should
|
||||||
|
differ from the proper name, this name can be
|
||||||
|
changed freely and may be used for
|
||||||
|
inside-jokes without issue.)
|
||||||
|
Description
|
||||||
|
Go to the golden palace and kill
|
||||||
|
the big dude with the ugly sunglasses
|
||||||
|
(Optional third argument for use as a more
|
||||||
|
detailed note or reminder of the objective
|
||||||
|
for the segment that may be displayed
|
||||||
|
alongside the name)
|
||||||
|
|
||||||
|
Route
|
||||||
|
Name
|
||||||
|
Magic Swordless (Name for the route, keeping in mind routes
|
||||||
|
are not categories, you may simply run
|
||||||
|
multiple different routes because you're unsure
|
||||||
|
yet which strategies are the fastest)
|
||||||
|
Segments
|
||||||
|
Stage One (The ordered list of segments the route
|
||||||
|
Stage Two consists of, these names should match the
|
||||||
|
Stage Four segment Shortnames)
|
||||||
|
|
||||||
|
Run Directives
|
||||||
|
--------------
|
||||||
|
These directives are much more complicated and are not intended to be written
|
||||||
|
by a human but rather by the timer software, they will make up the majority
|
||||||
|
of a file as they are the run history which may be quite long.
|
||||||
|
The data passed by these directives exists agnostic of segments, route, games,
|
||||||
|
or categories, rather they are either explicitly matched with metadata that is
|
||||||
|
applicable, or by default is matched with the last set of metadata declared by
|
||||||
|
the time of the run directive
|
||||||
|
|
||||||
|
Run
|
||||||
|
Route
|
||||||
|
Magic Swordless
|
||||||
|
Start
|
||||||
|
2016-10-23 10:03:12.034Z
|
||||||
|
(The first event in a run must be Start, and it
|
||||||
|
must be accompanied by an RFC 3339 timestamp)
|
||||||
|
Split
|
||||||
|
120052 (Following events may be accompanied by
|
||||||
|
RFC 3339 timestamps or simply a millisecond
|
||||||
|
offset from the previous event)
|
||||||
|
Skip
|
||||||
|
323481
|
||||||
|
Split
|
||||||
|
3121111
|
||||||
|
Pause
|
||||||
|
421397
|
||||||
|
Resume
|
||||||
|
2016-10-23 11:16:04.175Z
|
||||||
|
Stop (The last event in a run is always a Stop)
|
||||||
|
123111
|
||||||
|
|
||||||
|
The 'Then' Directive
|
||||||
|
------------------
|
||||||
|
The default behaviour the timer should exhibit when multiple games, catagories,
|
||||||
|
or routes exist in the same quest file is to allow the user to select
|
||||||
|
which one should be run, but the 'Then' directive instead allows for multiple
|
||||||
|
routes or catagories to be run one straight after the other as a single longer
|
||||||
|
"route". The timer should insert a dummy "segment" between the two routes for
|
||||||
|
the user to spend resetting the game and record the resulting run in two places:
|
||||||
|
A run directive should be recorded with every event including the dummy split
|
||||||
|
that can be exported and shared as one marathon attempt
|
||||||
|
AND
|
||||||
|
Individual run directives for each of the individual routes should be created
|
||||||
|
without the other included routes or the dummy split that can contribute to the
|
||||||
|
attempt history and stats of those individual catagories
|
||||||
|
If multiple routes exist on both or either side of a Then directive, the routes
|
||||||
|
to be stitched together should be selectable out of the options given, just as
|
||||||
|
they are normally.
|
@ -22,6 +22,7 @@ struct pastseg *pastRuns;
|
|||||||
int segCount;
|
int segCount;
|
||||||
int currSeg = -1;
|
int currSeg = -1;
|
||||||
char currentTime[10];
|
char currentTime[10];
|
||||||
|
int *route;
|
||||||
|
|
||||||
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td)
|
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
struct segment
|
struct segment
|
||||||
{
|
{
|
||||||
|
int id;
|
||||||
char *name;
|
char *name;
|
||||||
int ms;
|
int ms;
|
||||||
bool isSkipped;
|
bool isSkipped;
|
||||||
@ -49,6 +50,7 @@ extern struct segment *bestsegs;
|
|||||||
extern struct segment *wrrun;
|
extern struct segment *wrrun;
|
||||||
extern struct segment *segments;
|
extern struct segment *segments;
|
||||||
extern struct timespec notif;
|
extern struct timespec notif;
|
||||||
|
extern int *route;
|
||||||
|
|
||||||
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td);
|
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td);
|
||||||
void add_timespec(struct timespec t1, struct timespec t2, struct timespec* td);
|
void add_timespec(struct timespec t1, struct timespec t2, struct timespec* td);
|
||||||
|
73
src2/client.c
Normal file
73
src2/client.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int sockfd, portno, n;
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
char buffer[256];
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr,"usage %s command\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
portno = 8101;
|
||||||
|
|
||||||
|
/* Create a socket point */
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (sockfd < 0) {
|
||||||
|
perror("ERROR opening socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
server = gethostbyname("localhost");
|
||||||
|
|
||||||
|
if (server == NULL) {
|
||||||
|
fprintf(stderr,"ERROR, no such host\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
|
||||||
|
serv_addr.sin_port = htons(portno);
|
||||||
|
|
||||||
|
/* Now connect to the server */
|
||||||
|
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
perror("ERROR connecting");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(buffer, 256);
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
strcat(buffer, argv[i]);
|
||||||
|
strcat(buffer, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send message to the server */
|
||||||
|
n = write(sockfd, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR writing to socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now read server response */
|
||||||
|
bzero(buffer,256);
|
||||||
|
n = read(sockfd, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR reading from socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (buffer != NULL)
|
||||||
|
printf("%s\n", buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
820
src2/server.c
Normal file
820
src2/server.c
Normal file
@ -0,0 +1,820 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define NS_PER_S 1000000000
|
||||||
|
|
||||||
|
struct timespec finish, delta;
|
||||||
|
bool timerActive = false;
|
||||||
|
bool paused = false;
|
||||||
|
bool alive = true;
|
||||||
|
bool hasUndoneAtLeastOnce = false;
|
||||||
|
bool runUnsaved = false;
|
||||||
|
int timerOffset = 0;
|
||||||
|
enum event_type {
|
||||||
|
START,
|
||||||
|
SPLIT,
|
||||||
|
SKIP,
|
||||||
|
PAUSE,
|
||||||
|
RESUME,
|
||||||
|
RESET,
|
||||||
|
STOP
|
||||||
|
};
|
||||||
|
struct run_event {
|
||||||
|
enum event_type type;
|
||||||
|
struct timespec time;
|
||||||
|
};
|
||||||
|
struct segment {
|
||||||
|
char *shortname;
|
||||||
|
char *longname;
|
||||||
|
char *description;
|
||||||
|
};
|
||||||
|
struct route {
|
||||||
|
char *name;
|
||||||
|
int segment_count;
|
||||||
|
struct segment *segments;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct run_event *run;
|
||||||
|
//Enough to hold a sm64 16 star, can realloc later
|
||||||
|
int runMaxLength = 12;
|
||||||
|
int runMarker = 0;
|
||||||
|
int runMarker2 = 0;
|
||||||
|
|
||||||
|
//save file stuff
|
||||||
|
char *default_file_name = "untitled.quest";
|
||||||
|
int run_count = 0;
|
||||||
|
int files = 0;
|
||||||
|
char **filePaths = NULL;
|
||||||
|
char **meta_keys, **meta_values;
|
||||||
|
int valuecount;
|
||||||
|
struct segment *segments;
|
||||||
|
int segment_count = 0;
|
||||||
|
struct route *routes;
|
||||||
|
int route_count = 0;
|
||||||
|
struct route current_route;
|
||||||
|
|
||||||
|
//functions
|
||||||
|
void sub_timespec(struct timespec t1, struct timespec t2, struct timespec* td);
|
||||||
|
void offset_timespec(int milliseconds, struct timespec* t);
|
||||||
|
int timespecToMS(struct timespec t);
|
||||||
|
void extend_run();
|
||||||
|
void add_event(enum event_type t);
|
||||||
|
void appendRunToFile();
|
||||||
|
void timespecToRFC3339(struct timespec t, char buf[]);
|
||||||
|
void loadFiles();
|
||||||
|
void add_segment(char *sname, char *lname, char *desc);
|
||||||
|
void addFile(char *path);
|
||||||
|
void sendInt(int sock, int value);
|
||||||
|
void sendValue(int sock, char* name);
|
||||||
|
void sendString(int sock, char* str);
|
||||||
|
void process_socket_input(int sock);
|
||||||
|
void set_metadata(char *key, char *value);
|
||||||
|
void save_metadata_to_file(char *token, char *token2);
|
||||||
|
void reset_timer();
|
||||||
|
int current_ms();
|
||||||
|
|
||||||
|
//basic timer commands
|
||||||
|
void start();
|
||||||
|
void split();
|
||||||
|
void stop();
|
||||||
|
void skip();
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
void pause_timer();
|
||||||
|
void resume();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
//convenient combination commands
|
||||||
|
void start_split_stop();
|
||||||
|
void start_reset();
|
||||||
|
void start_split();
|
||||||
|
void split_stop();
|
||||||
|
void start_stop();
|
||||||
|
void pause_resume();
|
||||||
|
void undo_redo();
|
||||||
|
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void offset_timespec(int milliseconds, struct timespec* t)
|
||||||
|
{
|
||||||
|
//this should leave milliseconds with just the remainder
|
||||||
|
int second_offset = milliseconds / 1000;
|
||||||
|
milliseconds -= second_offset * 1000;
|
||||||
|
|
||||||
|
int nanosecond_offset = milliseconds * 1000000;
|
||||||
|
|
||||||
|
t->tv_nsec -= nanosecond_offset;
|
||||||
|
if (t->tv_nsec < 0) {
|
||||||
|
second_offset++;
|
||||||
|
t->tv_nsec += 1000000000;
|
||||||
|
}
|
||||||
|
t->tv_sec -= second_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timespecToMS(struct timespec t)
|
||||||
|
{
|
||||||
|
return (t.tv_nsec / 1000000) + (t.tv_sec * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void extend_run()
|
||||||
|
{
|
||||||
|
runMaxLength *= 2;
|
||||||
|
run = realloc(run, sizeof(struct run_event) * runMaxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_event(enum event_type t)
|
||||||
|
{
|
||||||
|
if (runMarker == runMaxLength)
|
||||||
|
extend_run();
|
||||||
|
run[runMarker].type = t;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &run[runMarker].time);
|
||||||
|
if (t == START)
|
||||||
|
offset_timespec(timerOffset, &run[runMarker].time);
|
||||||
|
runMarker++;
|
||||||
|
runMarker2 = runMarker;
|
||||||
|
hasUndoneAtLeastOnce = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_timer()
|
||||||
|
{
|
||||||
|
runMarker = 0;
|
||||||
|
runMarker2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
if (timerActive) return;
|
||||||
|
//Save the old run to the file before the new one starts,
|
||||||
|
//the reason to do this here is it gives the runner a chance to undo
|
||||||
|
//if they accidentally hit the stop button
|
||||||
|
appendRunToFile();
|
||||||
|
reset_timer();
|
||||||
|
timerActive = true;
|
||||||
|
add_event(START);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
timerActive = false;
|
||||||
|
add_event(STOP);
|
||||||
|
//this makes sure the time clients recieve from time
|
||||||
|
//requests match the time on the stop event
|
||||||
|
finish = run[runMarker - 1].time;
|
||||||
|
runUnsaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Identical function to stop() but with a RESET event
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
timerActive = false;
|
||||||
|
add_event(RESET);
|
||||||
|
finish = run[runMarker - 1].time;
|
||||||
|
runUnsaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_split_stop()
|
||||||
|
{
|
||||||
|
if (!timerActive) {
|
||||||
|
start();
|
||||||
|
} else {
|
||||||
|
if (runMarker < current_route.segment_count) {
|
||||||
|
split();
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_split()
|
||||||
|
{
|
||||||
|
if (!timerActive) start();
|
||||||
|
else split();
|
||||||
|
}
|
||||||
|
|
||||||
|
void split_stop()
|
||||||
|
{
|
||||||
|
if (runMarker < current_route.segment_count) split();
|
||||||
|
else stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_stop()
|
||||||
|
{
|
||||||
|
if (!timerActive) start();
|
||||||
|
else stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_reset()
|
||||||
|
{
|
||||||
|
if (!timerActive) start();
|
||||||
|
else reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void split()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
add_event(SPLIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
add_event(SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void undo()
|
||||||
|
{
|
||||||
|
if (runMarker > 0) {
|
||||||
|
runMarker--;
|
||||||
|
if (run[runMarker].type == STOP) {
|
||||||
|
timerActive = true;
|
||||||
|
runUnsaved = false;
|
||||||
|
}
|
||||||
|
if (run[runMarker].type == START)
|
||||||
|
timerActive = false;
|
||||||
|
if (run[runMarker].type == PAUSE)
|
||||||
|
paused = false;
|
||||||
|
if (run[runMarker].type == RESUME) {
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
hasUndoneAtLeastOnce = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void redo()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
if (runMarker < runMarker2) {
|
||||||
|
runMarker++;
|
||||||
|
if (run[runMarker - 1].type == STOP) {
|
||||||
|
timerActive = false;
|
||||||
|
runUnsaved = true;
|
||||||
|
finish = run[runMarker - 1].time;
|
||||||
|
}
|
||||||
|
if (run[runMarker - 1].type == START)
|
||||||
|
timerActive = true;
|
||||||
|
if (run[runMarker - 1].type == PAUSE)
|
||||||
|
paused = true;
|
||||||
|
if (run[runMarker - 1].type == RESUME) {
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (runMarker == runMarker2) {
|
||||||
|
hasUndoneAtLeastOnce = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void undo_redo()
|
||||||
|
{
|
||||||
|
if (hasUndoneAtLeastOnce) redo();
|
||||||
|
else undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
//this isnt just called pause() because that would overlap with <unistd.h>
|
||||||
|
void pause_timer()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
if (!paused) {
|
||||||
|
add_event(PAUSE);
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
if (!timerActive) return;
|
||||||
|
if (paused) {
|
||||||
|
add_event(RESUME);
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pause_resume()
|
||||||
|
{
|
||||||
|
if (paused) resume();
|
||||||
|
else pause_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendRunToFile()
|
||||||
|
{
|
||||||
|
if (!runUnsaved)
|
||||||
|
return;
|
||||||
|
char* save_path = NULL;
|
||||||
|
if (files <= 0)
|
||||||
|
save_path = default_file_name;
|
||||||
|
else
|
||||||
|
save_path = filePaths[0];
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
fp = fopen(save_path, "a+");
|
||||||
|
fprintf(fp, "%s\n", "Run");
|
||||||
|
if (current_route.name != NULL) {
|
||||||
|
fprintf(fp, "\t%s\n", "Route");
|
||||||
|
fprintf(fp, "\t\t%s\n", current_route.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
bool done = false;
|
||||||
|
while (!done) {
|
||||||
|
if (run[i].type == STOP) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
switch (run[i].type) {
|
||||||
|
case START:
|
||||||
|
fprintf(fp, "\t%s\n", "Start");
|
||||||
|
break;
|
||||||
|
case SPLIT:
|
||||||
|
fprintf(fp, "\t%s\n", "Split");
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
fprintf(fp, "\t%s\n", "Skip");
|
||||||
|
break;
|
||||||
|
case PAUSE:
|
||||||
|
fprintf(fp, "\t%s\n", "Pause");
|
||||||
|
break;
|
||||||
|
case RESUME:
|
||||||
|
fprintf(fp, "\t%s\n", "Resume");
|
||||||
|
break;
|
||||||
|
case RESET:
|
||||||
|
fprintf(fp, "\t%s\n", "Reset");
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
fprintf(fp, "\t%s\n", "Stop");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
char buf[25];
|
||||||
|
timespecToRFC3339(run[i].time, buf);
|
||||||
|
fprintf(fp, "\t\t%s\n", buf);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sub_timespec(run[i - 1].time, run[i].time, &delta);
|
||||||
|
fprintf(fp, "\t\t%d\n", timespecToMS(delta));
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
fclose(fp);
|
||||||
|
run_count++;
|
||||||
|
runUnsaved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timespecToRFC3339(struct timespec t, char buf[])
|
||||||
|
{
|
||||||
|
const int tmpsize = 21;
|
||||||
|
struct tm tm;
|
||||||
|
gmtime_r(&t.tv_sec, &tm);
|
||||||
|
strftime(buf, tmpsize, "%Y-%m-%d %H:%M:%S.", &tm);
|
||||||
|
sprintf(buf + tmpsize - 1, "%03luZ", (t.tv_nsec / 1000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFiles()
|
||||||
|
{
|
||||||
|
FILE* fp;
|
||||||
|
char buff[255];
|
||||||
|
char buff2[255];
|
||||||
|
|
||||||
|
for (int i = 0; i < files; i++) {
|
||||||
|
fp = fopen(filePaths[i], "r+");
|
||||||
|
while(1) {
|
||||||
|
if (!fgets(buff, 255, fp))
|
||||||
|
break;
|
||||||
|
if (buff[0] == '/' && buff[1] == '/' || buff[0] == '\n' || buff[0] == '\t')
|
||||||
|
continue;
|
||||||
|
if (!strcmp(buff, "Segment\n")) {
|
||||||
|
char *s = NULL;
|
||||||
|
char *l = NULL;
|
||||||
|
char *d = NULL;
|
||||||
|
for (int x = 0; x < 3; x++) {
|
||||||
|
if (!fgets(buff2, 255, fp))
|
||||||
|
break;
|
||||||
|
if (!strcmp(buff2, "\tShortname\n")) {
|
||||||
|
if (!fgets(buff2, 255, fp))
|
||||||
|
break;
|
||||||
|
s = malloc(strlen(buff2) - 2);
|
||||||
|
s = strncpy(s, buff2 + 2, strlen(buff2) - 2);
|
||||||
|
s[strlen(s) - 1] = '\0';
|
||||||
|
} else if (!strcmp(buff2, "\tLongname\n")) {
|
||||||
|
if (!fgets(buff2, 255, fp))
|
||||||
|
break;
|
||||||
|
l = malloc(strlen(buff2) - 2);
|
||||||
|
l = strncpy(l, buff2 + 2, strlen(buff2) - 2);
|
||||||
|
l[strlen(l) - 1] = '\0';
|
||||||
|
} else if (!strcmp(buff2, "\tDescription\n")) {
|
||||||
|
if (!fgets(buff2, 255, fp))
|
||||||
|
break;
|
||||||
|
d = malloc(strlen(buff2) - 2);
|
||||||
|
d = strncpy(d, buff2 + 2, strlen(buff2) - 2);
|
||||||
|
d[strlen(d) - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_segment(s, l, d);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(buff, "Route\n"))
|
||||||
|
continue;
|
||||||
|
if (!strcmp(buff, "Run\n")) {
|
||||||
|
run_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!fgets(buff2, 255, fp))
|
||||||
|
break;
|
||||||
|
if (buff2[0] == '\t') {
|
||||||
|
buff[strlen(buff) - 1] = '\0';
|
||||||
|
buff2[strlen(buff2) - 1] = '\0';
|
||||||
|
set_metadata(buff, buff2 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_segment(char *sname, char *lname, char *desc)
|
||||||
|
{
|
||||||
|
segment_count++;
|
||||||
|
segments = realloc(segments, sizeof(struct segment) * segment_count);
|
||||||
|
segments[segment_count - 1].shortname = sname;
|
||||||
|
segments[segment_count - 1].longname = lname;
|
||||||
|
segments[segment_count - 1].description = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: eventually file loading should support loading multiple files
|
||||||
|
void addFile(char *path)
|
||||||
|
{
|
||||||
|
files++;
|
||||||
|
filePaths = realloc(filePaths, sizeof(char*) * files);
|
||||||
|
filePaths[files - 1] = path;
|
||||||
|
loadFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
int current_ms()
|
||||||
|
{
|
||||||
|
if (timerActive)
|
||||||
|
clock_gettime(CLOCK_REALTIME, &finish);
|
||||||
|
//if (paused) {
|
||||||
|
// sub_timespec(run[0].time, run[runMarker - 1].time, &delta);
|
||||||
|
//} else {
|
||||||
|
sub_timespec(run[0].time, finish, &delta);
|
||||||
|
//}
|
||||||
|
return timespecToMS(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendInt(int sock, int value)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
sprintf(buffer, "%d", value);
|
||||||
|
int n = write(sock, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR writing to socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendValue(int sock, char* name)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
int n, x;
|
||||||
|
bool namefound = false;
|
||||||
|
if (name == NULL) {
|
||||||
|
strcpy(buffer, "DATA NOT PRESENT");
|
||||||
|
} else {
|
||||||
|
for(int i = 0; i < valuecount; i++) {
|
||||||
|
if (!strcmp(meta_keys[i], name)) {
|
||||||
|
x = i;
|
||||||
|
namefound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (namefound)
|
||||||
|
strcpy(buffer, meta_values[x]);
|
||||||
|
else
|
||||||
|
strcpy(buffer, "DATA NOT PRESENT");
|
||||||
|
}
|
||||||
|
n = write(sock, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR writing to socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendString(int sock, char* str)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
strcpy(buffer, str);
|
||||||
|
int n = write(sock, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR writing to socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_metadata(char *key, char *value)
|
||||||
|
{
|
||||||
|
char key_pos = -1;
|
||||||
|
for (int i = 0; i < valuecount; i++)
|
||||||
|
if (!strcmp(meta_keys[i], key))
|
||||||
|
key_pos = i;
|
||||||
|
if (key_pos > -1) {
|
||||||
|
meta_values[key_pos] = realloc(meta_values[key_pos], strlen(value));
|
||||||
|
strncpy(meta_values[key_pos], value, strlen(value));
|
||||||
|
meta_values[key_pos][strlen(value)] = '\0';
|
||||||
|
} else {
|
||||||
|
valuecount++;
|
||||||
|
|
||||||
|
meta_keys = realloc(meta_keys, sizeof(char*) * valuecount);
|
||||||
|
meta_keys[valuecount - 1] = malloc(strlen(key));
|
||||||
|
strncpy(meta_keys[valuecount - 1], key, strlen(key));
|
||||||
|
meta_keys[valuecount - 1][strlen(key)] = '\0';
|
||||||
|
|
||||||
|
meta_values = realloc(meta_values, sizeof(char*) * valuecount);
|
||||||
|
meta_values[valuecount - 1] = malloc(strlen(value));
|
||||||
|
strncpy(meta_values[valuecount - 1], value, strlen(value));
|
||||||
|
meta_values[valuecount - 1][strlen(value)] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_metadata_to_file(char *token, char *token2)
|
||||||
|
{
|
||||||
|
char* save_path = NULL;
|
||||||
|
if (files <= 0)
|
||||||
|
save_path = default_file_name;
|
||||||
|
else
|
||||||
|
save_path = filePaths[0];
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
fp = fopen(save_path, "r+");
|
||||||
|
fprintf(fp, "%s\n", token);
|
||||||
|
fprintf(fp, "\t%s\n\n", token2);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_socket_input(int sock)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char buffer[256];
|
||||||
|
n = read(sock, &buffer, 256);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("ERROR reading from socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
char *token = strtok(buffer, " ");
|
||||||
|
|
||||||
|
//Imperative commands
|
||||||
|
if (!strcmp(token, "start")) {
|
||||||
|
start();
|
||||||
|
} else if (!strcmp(token, "stop")) {
|
||||||
|
stop();
|
||||||
|
} else if (!strcmp(token, "reset")) {
|
||||||
|
reset();
|
||||||
|
} else if (!strcmp(token, "kill")) {
|
||||||
|
alive = false;
|
||||||
|
} else if (!strcmp(token, "split")) {
|
||||||
|
split();
|
||||||
|
} else if (!strcmp(token, "skip")) {
|
||||||
|
skip();
|
||||||
|
} else if (!strcmp(token, "pause")) {
|
||||||
|
pause_timer();
|
||||||
|
} else if (!strcmp(token, "resume")) {
|
||||||
|
resume();
|
||||||
|
} else if (!strcmp(token, "undo")) {
|
||||||
|
undo();
|
||||||
|
} else if (!strcmp(token, "redo")) {
|
||||||
|
redo();
|
||||||
|
} else if (!strcmp(token, "save")) {
|
||||||
|
appendRunToFile();
|
||||||
|
} else if (!strcmp(token, "start-split-stop")) {
|
||||||
|
start_split_stop();
|
||||||
|
} else if (!strcmp(token, "pause-resume")) {
|
||||||
|
pause_resume();
|
||||||
|
} else if (!strcmp(token, "start-stop")) {
|
||||||
|
start_stop();
|
||||||
|
} else if (!strcmp(token, "start-reset")) {
|
||||||
|
start_reset();
|
||||||
|
} else if (!strcmp(token, "start-split")) {
|
||||||
|
start_split();
|
||||||
|
} else if (!strcmp(token, "split-stop")) {
|
||||||
|
split_stop();
|
||||||
|
} else if (!strcmp(token, "undo-redo")) {
|
||||||
|
undo_redo();
|
||||||
|
|
||||||
|
//Getters
|
||||||
|
} else if (!strcmp(token, "get")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
if (!strcmp(token, "current_time")) {
|
||||||
|
sendInt(sock, current_ms());
|
||||||
|
} else if (!strcmp(token, "current_time_with_pause")) {
|
||||||
|
int running_pause = 0;
|
||||||
|
struct timespec p, r;
|
||||||
|
bool tracking_pause = false;
|
||||||
|
for (int i = 0; i < runMarker; i++) {
|
||||||
|
if (run[i].type == PAUSE) {
|
||||||
|
sub_timespec(run[0].time, run[i].time, &p);
|
||||||
|
tracking_pause = true;
|
||||||
|
}
|
||||||
|
if (run[i].type == RESUME) {
|
||||||
|
sub_timespec(run[0].time, run[i].time, &r);
|
||||||
|
running_pause += timespecToMS(r) - timespecToMS(p);
|
||||||
|
tracking_pause = false;
|
||||||
|
} else if (i == runMarker - 1 && tracking_pause) {
|
||||||
|
running_pause += current_ms() - timespecToMS(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendInt(sock, current_ms() - running_pause);
|
||||||
|
} else if (!strcmp(token, "run_count")) {
|
||||||
|
sendInt(sock, run_count);
|
||||||
|
} else if (!strcmp(token, "segment_count")) {
|
||||||
|
sendInt(sock, segment_count);
|
||||||
|
} else if (!strcmp(token, "route_count")) {
|
||||||
|
sendInt(sock, route_count);
|
||||||
|
} else if (!strcmp(token, "event_count")) {
|
||||||
|
sendInt(sock, runMarker);
|
||||||
|
} else if (!strcmp(token, "segment_shortname")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
sendString(sock, segments[x].shortname);
|
||||||
|
} else if (!strcmp(token, "segment_longname")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
sendString(sock, segments[x].longname);
|
||||||
|
} else if (!strcmp(token, "segment_description")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
sendString(sock, segments[x].description);
|
||||||
|
} else if (!strcmp(token, "route_name")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
sendString(sock, routes[x].name);
|
||||||
|
} else if (!strcmp(token, "route_segment_count")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
sendInt(sock, routes[x].segment_count);
|
||||||
|
} else if (!strcmp(token, "route_segment_shortname")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x = atoi(token);
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int y = atoi(token);
|
||||||
|
sendString(sock, routes[x].segments[y].shortname);
|
||||||
|
} else if (!strcmp(token, "event_time")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x;
|
||||||
|
if (!strcmp(token, "last"))
|
||||||
|
x = runMarker - 1;
|
||||||
|
else if (!strcmp(token, "first"))
|
||||||
|
x = 0;
|
||||||
|
else
|
||||||
|
x = atoi(token);
|
||||||
|
struct timespec t;
|
||||||
|
sub_timespec(run[0].time, run[x].time, &t);
|
||||||
|
sendInt(sock, timespecToMS(t));
|
||||||
|
} else if (!strcmp(token, "event_time_with_pause")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x;
|
||||||
|
if (!strcmp(token, "last"))
|
||||||
|
x = runMarker - 1;
|
||||||
|
else if (!strcmp(token, "first"))
|
||||||
|
x = 0;
|
||||||
|
else
|
||||||
|
x = atoi(token);
|
||||||
|
int running_pause = 0;
|
||||||
|
struct timespec p, r;
|
||||||
|
bool tracking_pause = false;
|
||||||
|
for (int i = 0; i < x; i++) {
|
||||||
|
if (run[i].type == PAUSE) {
|
||||||
|
sub_timespec(run[0].time, run[i].time, &p);
|
||||||
|
tracking_pause = true;
|
||||||
|
}
|
||||||
|
if (run[i].type == RESUME) {
|
||||||
|
sub_timespec(run[0].time, run[i].time, &r);
|
||||||
|
running_pause += timespecToMS(r) - timespecToMS(p);
|
||||||
|
tracking_pause = false;
|
||||||
|
} else if (i == x - 1 && tracking_pause) {
|
||||||
|
sub_timespec(run[0].time, run[x].time, &r);
|
||||||
|
running_pause += timespecToMS(r) - timespecToMS(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct timespec t;
|
||||||
|
sub_timespec(run[0].time, run[x].time, &t);
|
||||||
|
sendInt(sock, timespecToMS(t) - running_pause);
|
||||||
|
} else if (!strcmp(token, "event_type")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
int x;
|
||||||
|
if (!strcmp(token, "last"))
|
||||||
|
x = runMarker - 1;
|
||||||
|
else if (!strcmp(token, "first"))
|
||||||
|
x = 0;
|
||||||
|
else
|
||||||
|
x = atoi(token);
|
||||||
|
char *reply;
|
||||||
|
switch (run[x].type) {
|
||||||
|
case START:
|
||||||
|
reply = "START";
|
||||||
|
break;
|
||||||
|
case SPLIT:
|
||||||
|
reply = "SPLIT";
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
reply = "SKIP";
|
||||||
|
break;
|
||||||
|
case PAUSE:
|
||||||
|
reply = "PAUSE";
|
||||||
|
break;
|
||||||
|
case RESUME:
|
||||||
|
reply = "RESUME";
|
||||||
|
break;
|
||||||
|
case RESET:
|
||||||
|
reply = "RESET";
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
reply = "STOP";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendString(sock, reply);
|
||||||
|
} else if (!strcmp(token, "meta")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
sendValue(sock, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Setters
|
||||||
|
} else if (!strcmp(token, "set")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
if (!strcmp(token, "meta")) {
|
||||||
|
token = strtok(NULL, " ");
|
||||||
|
char *token2 = strtok(NULL, " ");
|
||||||
|
set_metadata(token, token2);
|
||||||
|
save_metadata_to_file(token, token2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int sockfd, newsockfd, portno, clilen;
|
||||||
|
char buffer[256];
|
||||||
|
struct sockaddr_in serv_addr, cli_addr;
|
||||||
|
int n, pid;
|
||||||
|
run = malloc(sizeof(struct run_event) * runMaxLength);
|
||||||
|
//TODO: remove this file testing boilerplate
|
||||||
|
if (argc > 1)
|
||||||
|
addFile(argv[1]);
|
||||||
|
|
||||||
|
/* First call to socket() function */
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (sockfd < 0) {
|
||||||
|
perror("ERROR opening socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize socket structure */
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
portno = 8101;
|
||||||
|
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
serv_addr.sin_port = htons(portno);
|
||||||
|
|
||||||
|
/* Now bind the host address using bind() call.*/
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
perror("ERROR on binding");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(sockfd,5);
|
||||||
|
clilen = sizeof(cli_addr);
|
||||||
|
|
||||||
|
printf("Ready!\n");
|
||||||
|
while (alive) {
|
||||||
|
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||||
|
if (newsockfd < 0) {
|
||||||
|
perror("ERROR on accept");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
process_socket_input(newsockfd);
|
||||||
|
close(newsockfd);
|
||||||
|
}
|
||||||
|
free(run);
|
||||||
|
close(sockfd);
|
||||||
|
}
|
||||||
|
|
237
src2/tui.c
Normal file
237
src2/tui.c
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
char numbermap[5][44] = {
|
||||||
|
"xxx..x..xxx.xxx.x.x.xxx.xxx.xxx.xxx.xxx.....",
|
||||||
|
"x.x..x....x...x.x.x.x...x.....x.x.x.x.x.x...",
|
||||||
|
"x.x..x..xxx.xxx.xxx.xxx.xxx...x.xxx.xxx.....",
|
||||||
|
"x.x..x..x.....x...x...x.x.x...x.x.x...x.x...",
|
||||||
|
"xxx..x..xxx.xxx...x.xxx.xxx...x.xxx...x...x."
|
||||||
|
};
|
||||||
|
struct termios base;
|
||||||
|
int fps = 60;
|
||||||
|
struct color {
|
||||||
|
int r;
|
||||||
|
int g;
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
struct color b = { 47, 53, 66}; //Background color
|
||||||
|
struct color f = {247, 248, 242}; //Text foreground color
|
||||||
|
struct color g = {249, 255, 79}; //Best ever segment time color
|
||||||
|
struct color ag = { 24, 240, 31}; //Ahead, and gaining time segment color
|
||||||
|
struct color al = { 79, 255, 85}; //Ahead, but losing time segment color
|
||||||
|
struct color bg = {255, 79, 79}; //Behind, but gaining time segment color
|
||||||
|
struct color bl = {224, 34, 34}; //Behind, and losing time segment color
|
||||||
|
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
//functions
|
||||||
|
void timestring(char *str, int ms);
|
||||||
|
void printbig(int x, int y, int ms);
|
||||||
|
int timestringDigits(int ms);
|
||||||
|
void resize(int i);
|
||||||
|
void initScreen();
|
||||||
|
void resetScreen();
|
||||||
|
void die(int i);
|
||||||
|
void processColorString(struct color *c, char* s);
|
||||||
|
|
||||||
|
|
||||||
|
int timestringDigits(int ms)
|
||||||
|
{
|
||||||
|
int chars = 4;
|
||||||
|
if (ms >= 10000)
|
||||||
|
chars += 1;
|
||||||
|
if (ms >= 60000)
|
||||||
|
chars += 2;
|
||||||
|
if (ms >= 600000)
|
||||||
|
chars += 1;
|
||||||
|
if (ms >= 3600000)
|
||||||
|
chars += 2;
|
||||||
|
if (ms >= 36000000)
|
||||||
|
chars += 1;
|
||||||
|
if (ms >= 360000000)
|
||||||
|
chars += 1;
|
||||||
|
return chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attempt 2 at thinking through how to print the big numbies
|
||||||
|
void printbig(int x, int y, int ms)
|
||||||
|
{
|
||||||
|
char small[13];
|
||||||
|
timestring(small, ms);
|
||||||
|
if (w < strlen(small)) {
|
||||||
|
printf("2smol\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int bigstringw = 42; //Minimum width and height the big timer string
|
||||||
|
int bigstringh = 5; //bigger sizes are just multiples of these numbers.
|
||||||
|
int bigstrings = 1;
|
||||||
|
x = (w - (bigstringw - 1 * bigstrings)) / 2; //theres a -1 because theres extra whitespace on the last printed digit
|
||||||
|
y = (h - (bigstringh * bigstrings)) / 2;
|
||||||
|
for (int sy = 0; sy < 5; sy++) { //for every row
|
||||||
|
printf("\033[%d;%dH", y + sy, x); //go to position
|
||||||
|
for (int cc = 0; cc < 12; cc++) { //then, for every character
|
||||||
|
int c = small[cc]; //check what character we're on
|
||||||
|
int mapcharacterwidth = (c >= 48 && c <= 57) ? 4 : 2;//if its a number, print 4 pixels, if its punctuation, print 2 pixels
|
||||||
|
int mapoffset;
|
||||||
|
if (c >= 48 && c <= 57)
|
||||||
|
mapoffset = (c - 48) * 4;
|
||||||
|
else if (c == 46)
|
||||||
|
mapoffset = 42;
|
||||||
|
else
|
||||||
|
mapoffset = 40;
|
||||||
|
for (int xx = 0; xx < mapcharacterwidth; xx++) {
|
||||||
|
if (numbermap[sy][mapoffset + xx] == 'x')
|
||||||
|
printf("\033[48;2;%d;%d;%dm ", f.r, f.g, f.b);
|
||||||
|
if (numbermap[sy][mapoffset + xx] == '.')
|
||||||
|
printf("\033[48;2;%d;%d;%dm ", b.r, b.g, b.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
//printf("\033[%d;%dH%s\n", y, x, small + (12 - timestringDigits(time)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void timestring(char *str, int ms)
|
||||||
|
{
|
||||||
|
int msdigits = (ms / 10) % 100;
|
||||||
|
int seconds = (ms / 1000) % 60;
|
||||||
|
int minutes = ((ms / 1000) / 60) % 60;
|
||||||
|
int hours = ((ms / 1000) / 60) / 60;
|
||||||
|
sprintf(str, "%03d:%02d:%02d.%02d", hours, minutes, seconds, msdigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(int i)
|
||||||
|
{
|
||||||
|
struct winsize ws;
|
||||||
|
ioctl(1, TIOCGWINSZ, &ws);
|
||||||
|
w = ws.ws_col;
|
||||||
|
h = ws.ws_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initScreen()
|
||||||
|
{
|
||||||
|
struct termios t;
|
||||||
|
tcgetattr(1, &base);
|
||||||
|
t = base;
|
||||||
|
t.c_lflag &= (~ECHO & ~ICANON);
|
||||||
|
tcsetattr(1, TCSANOW, &t);
|
||||||
|
//TODO:Figure out why i did this
|
||||||
|
//dup(0);
|
||||||
|
fcntl(0, F_SETFL, O_NONBLOCK);
|
||||||
|
printf("\033[?1049h\n"); //Switch to TUI mode (alternate buffer)
|
||||||
|
printf("\033[?25l\n"); //Hide text cursor
|
||||||
|
printf("\033[2J\n"); //Clear screen
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetScreen()
|
||||||
|
{
|
||||||
|
tcsetattr(1, TCSANOW, &base);
|
||||||
|
printf("\033[2J\n"); //Clear screen
|
||||||
|
printf("\033[?1049l\n"); //Switch back to regular mode
|
||||||
|
printf("\033[?25h\n"); //Show cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
void die(int i)
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processColorString(struct color *c, char* s)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int length = strlen(s);
|
||||||
|
char comp[4];
|
||||||
|
int compcount = 0;
|
||||||
|
int colorcompsdone = 0;
|
||||||
|
//TODO: if we know the length now that we're not doing fgetc we dont
|
||||||
|
//need a while loop; convert to for loop.
|
||||||
|
//Why? what makes a for loop better than a while loop?
|
||||||
|
while (1) {
|
||||||
|
char x = s[i++];
|
||||||
|
if (x >= 48 && x <= 57) {
|
||||||
|
comp[compcount] = x;
|
||||||
|
compcount++;
|
||||||
|
}
|
||||||
|
if (x == 44 || i == length) {
|
||||||
|
comp[compcount] = '\0';
|
||||||
|
switch(colorcompsdone) {
|
||||||
|
case 0:
|
||||||
|
c->r = atoi(comp);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
c->g = atoi(comp);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
c->b = atoi(comp);
|
||||||
|
}
|
||||||
|
colorcompsdone++;
|
||||||
|
compcount = 0;
|
||||||
|
}
|
||||||
|
if (i == length)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
initScreen();
|
||||||
|
atexit(resetScreen);
|
||||||
|
signal(SIGTERM, die);
|
||||||
|
signal(SIGINT, die);
|
||||||
|
signal(SIGWINCH, resize);
|
||||||
|
resize(0);
|
||||||
|
|
||||||
|
FILE *fp;
|
||||||
|
char path[1000];
|
||||||
|
char ti[13];
|
||||||
|
|
||||||
|
//Request foreground color from config file
|
||||||
|
fp = popen("./result/bin/quest-log Foreground-Color", "r");
|
||||||
|
if (fgets(path, sizeof(path), fp)) {
|
||||||
|
if (strcmp(path, "DATA NOT PRESENT"))
|
||||||
|
processColorString(&f, path);
|
||||||
|
printf("\033[38;2;%d;%d;%dm", f.r, f.g, f.b);
|
||||||
|
}
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
//Request background color from config file
|
||||||
|
fp = popen("./result/bin/quest-log Background-Color", "r");
|
||||||
|
if (fgets(path, sizeof(path), fp)) {
|
||||||
|
if (strcmp(path, "DATA NOT PRESENT"))
|
||||||
|
processColorString(&b, path);
|
||||||
|
printf("\033[48;2;%d;%d;%dm", b.r, b.g, b.b);
|
||||||
|
}
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
//Set fps from command line argument
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "-fps"))
|
||||||
|
fps = atoi(argv[i + 1]);
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
int time = 0;
|
||||||
|
fp = popen("./result/bin/quest-log current_time", "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Failed to run command\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
//TODO: why is this a while loop?
|
||||||
|
while (fgets(path, sizeof(path), fp) != NULL) {
|
||||||
|
time = atoi(path);
|
||||||
|
}
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
printf("\033[2J\n");
|
||||||
|
//timestring(&ti, time);
|
||||||
|
//printf("%s\n", ti + (12 - timestringDigits(time)));
|
||||||
|
printbig(3, 4, time);
|
||||||
|
usleep(1000000 / fps);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user