Initial commit
commit
9192d614d2
|
@ -0,0 +1,21 @@
|
||||||
|
SRCS := lfsdash.c
|
||||||
|
SRCS += audio.c
|
||||||
|
SRCS += cars.c
|
||||||
|
SRCS += config.c
|
||||||
|
SRCS += gauge.c
|
||||||
|
SRCS += insim.c
|
||||||
|
SRCS += network_worker.c
|
||||||
|
SRCS += outgauge.c
|
||||||
|
SRCS += queue.c
|
||||||
|
SRCS += socket.c
|
||||||
|
SRCS += text.c
|
||||||
|
SRCS += timer.c
|
||||||
|
SRCS += worker.c
|
||||||
|
|
||||||
|
OBJS := $(SRCS:.c=.o)
|
||||||
|
|
||||||
|
CFLAGS += `pkg-config libglfw ftgl openal --cflags` -g
|
||||||
|
LDFLAGS += `pkg-config libglfw ftgl openal --libs` -g
|
||||||
|
|
||||||
|
lfsdash: $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) $(OBJS) -o $@
|
|
@ -0,0 +1,200 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
//#include <GL/glut.h>
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "audio.h"
|
||||||
|
|
||||||
|
#define NUM_BUFFERS 5
|
||||||
|
#define NUM_SOURCES 6
|
||||||
|
#define NUM_ENVIRONMENTS 1
|
||||||
|
|
||||||
|
ALCdevice *dev;
|
||||||
|
ALCcontext *ctx;
|
||||||
|
|
||||||
|
//There is only one listener, and these arrays define the initial set up of the listener for its position, direction, and velocity. The listener is not moving at the start, but can move around any other sound source.
|
||||||
|
ALfloat listenerPos[]={0.0,0.0,4.0};
|
||||||
|
ALfloat listenerVel[]={0.0,0.0,0.0};
|
||||||
|
ALfloat listenerOri[]={0.0,0.0,1.0, 0.0,1.0,0.0};
|
||||||
|
|
||||||
|
//Each sound source has similar properties that the listener has. The source0Pos and source0Vel arrays show the position and velocity of the sound source.
|
||||||
|
ALfloat source0Pos[]={ -2.0, 0.0, 0.0};
|
||||||
|
ALfloat source0Vel[]={ 0.0, 0.0, 0.0};
|
||||||
|
|
||||||
|
//Sounds need to be stored in an array, similar to textures. Several buffers are needed to contain the sounds and other necessary information. The size, freq, format, and data variables are used when loading the sound files.
|
||||||
|
ALuint buffer[NUM_BUFFERS];
|
||||||
|
ALuint source[NUM_SOURCES];
|
||||||
|
ALuint environment[NUM_ENVIRONMENTS];
|
||||||
|
|
||||||
|
int file_read_int32_le(char *buffer, FILE *file)
|
||||||
|
{
|
||||||
|
int r = fread(buffer, sizeof(char), 4, file);
|
||||||
|
uint32_t ret = (uint8_t)buffer[0];
|
||||||
|
ret |= (uint8_t)buffer[1] << 8;
|
||||||
|
ret |= (uint8_t)buffer[2] << 16;
|
||||||
|
ret |= (uint8_t)buffer[3] << 24;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_read_int16_le(char *buffer, FILE *file)
|
||||||
|
{
|
||||||
|
int r = fread(buffer, sizeof(char), 2, file);
|
||||||
|
uint16_t ret = (uint8_t)buffer[0];
|
||||||
|
ret |= (uint8_t)buffer[1] << 8;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_ignore_bytes(FILE *file, int bytes)
|
||||||
|
{
|
||||||
|
fseek(file, bytes, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *file_allocate_and_read_bytes(FILE *file, size_t bytes)
|
||||||
|
{
|
||||||
|
void *buffer = malloc(bytes);
|
||||||
|
fread(buffer, 1, bytes, file);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALenum GetFormatFromInfo(short channels, short bitsPerSample) {
|
||||||
|
if (channels == 1)
|
||||||
|
return AL_FORMAT_MONO16;
|
||||||
|
return AL_FORMAT_STEREO16;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_load(const char *filename, ALuint buffer)
|
||||||
|
{
|
||||||
|
FILE* file = fopen(filename, "rb");
|
||||||
|
char xbuffer[5];
|
||||||
|
memset(xbuffer, 0, sizeof xbuffer);
|
||||||
|
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "RIFF") != 0)
|
||||||
|
printf("Not a WAV file: %s\n", xbuffer);
|
||||||
|
|
||||||
|
file_read_int32_le(xbuffer, file);
|
||||||
|
|
||||||
|
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "WAVE") != 0)
|
||||||
|
printf("Not a WAV file: %s\n", xbuffer);
|
||||||
|
|
||||||
|
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "fmt ") != 0)
|
||||||
|
printf("Invalid WAV file: %s\n", xbuffer);
|
||||||
|
|
||||||
|
int size = file_read_int32_le(xbuffer, file);
|
||||||
|
short audioFormat = file_read_int16_le(xbuffer, file);
|
||||||
|
short channels = file_read_int16_le(xbuffer, file);
|
||||||
|
int sampleRate = file_read_int32_le(xbuffer, file);
|
||||||
|
int byteRate = file_read_int32_le(xbuffer, file);
|
||||||
|
file_read_int16_le(xbuffer, file);
|
||||||
|
short bitsPerSample = file_read_int16_le(xbuffer, file);
|
||||||
|
|
||||||
|
size -= 16;
|
||||||
|
|
||||||
|
file_ignore_bytes(file, size);
|
||||||
|
|
||||||
|
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "data") != 0)
|
||||||
|
printf("Invalid WAV file: %s\n", xbuffer);
|
||||||
|
|
||||||
|
int dataChunkSize = file_read_int32_le(xbuffer, file);
|
||||||
|
unsigned char* bufferData = file_allocate_and_read_bytes(file, (size_t) dataChunkSize);
|
||||||
|
|
||||||
|
float duration = (float)(dataChunkSize) / byteRate;
|
||||||
|
alBufferData(buffer, GetFormatFromInfo(channels, bitsPerSample), bufferData, dataChunkSize, sampleRate);
|
||||||
|
free(bufferData);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_init(void)
|
||||||
|
{
|
||||||
|
dev = alcOpenDevice(NULL);
|
||||||
|
if (!dev)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Oops\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = alcCreateContext(dev, NULL);
|
||||||
|
alcMakeContextCurrent(ctx);
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Oops2\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
alListenerfv(AL_POSITION,listenerPos);
|
||||||
|
alListenerfv(AL_VELOCITY,listenerVel);
|
||||||
|
alListenerfv(AL_ORIENTATION,listenerOri);
|
||||||
|
|
||||||
|
alGetError(); // clear any error messages
|
||||||
|
|
||||||
|
// Generate buffers, or else no sound will happen!
|
||||||
|
alGenBuffers(NUM_BUFFERS, buffer);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR) {
|
||||||
|
printf("- Error creating buffers !!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_load("off.wav", buffer[FX_OFF]);
|
||||||
|
audio_load("on.wav", buffer[FX_ON]);
|
||||||
|
audio_load("bing.wav", buffer[FX_BING]);
|
||||||
|
audio_load("bip.wav", buffer[FX_BIP]);
|
||||||
|
audio_load("bop.wav", buffer[FX_BOP]);
|
||||||
|
|
||||||
|
alGetError(); /* clear error */
|
||||||
|
alGenSources(NUM_SOURCES, source);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR)
|
||||||
|
{
|
||||||
|
printf("- Error creating sources !!\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ii;
|
||||||
|
for (ii = 0; ii < NUM_SOURCES; ii++)
|
||||||
|
{
|
||||||
|
alSourcef(source[ii], AL_PITCH, 1.0f);
|
||||||
|
alSourcef(source[ii], AL_GAIN, 1.0f);
|
||||||
|
alSourcefv(source[ii], AL_POSITION, source0Pos);
|
||||||
|
alSourcefv(source[ii], AL_VELOCITY, source0Vel);
|
||||||
|
//alSourcei(source[0], AL_BUFFER, buffer[0]);
|
||||||
|
alSourcei(source[ii], AL_LOOPING, AL_FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_deinit(void)
|
||||||
|
{
|
||||||
|
int ii;
|
||||||
|
for (ii = 0; ii < NUM_SOURCES; ii++)
|
||||||
|
{
|
||||||
|
alSourceStop(source[ii]);
|
||||||
|
}
|
||||||
|
|
||||||
|
alDeleteSources(NUM_SOURCES, source);
|
||||||
|
alDeleteBuffers(NUM_BUFFERS, buffer);
|
||||||
|
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(ctx);
|
||||||
|
alcCloseDevice(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_volume(float f)
|
||||||
|
{
|
||||||
|
int ii;
|
||||||
|
for (ii = 0; ii < NUM_SOURCES; ii++)
|
||||||
|
{
|
||||||
|
alSourcef(source[ii], AL_GAIN, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float frand(float mult)
|
||||||
|
{
|
||||||
|
return ((float)rand() / (float)RAND_MAX) * mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_play(int s, int b)
|
||||||
|
{
|
||||||
|
alSourceStop(source[s]);
|
||||||
|
alSourcei(source[s], AL_BUFFER, buffer[b]);
|
||||||
|
alSourcePlay(source[s]);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef AUDIO_H
|
||||||
|
#define AUDIO_H
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FX_ON,
|
||||||
|
FX_OFF,
|
||||||
|
FX_BING,
|
||||||
|
FX_BIP,
|
||||||
|
FX_BOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
void audio_init(void);
|
||||||
|
void audio_deinit(void);
|
||||||
|
void audio_volume(float f);
|
||||||
|
void audio_play(int s, int on);
|
||||||
|
|
||||||
|
#endif /* AUDIO_H */
|
|
@ -0,0 +1,119 @@
|
||||||
|
#include "cars.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct car s_cars[MAX_CARS];
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
char* strsep(char** stringp, const char* delim)
|
||||||
|
{
|
||||||
|
char* start = *stringp;
|
||||||
|
char* p;
|
||||||
|
|
||||||
|
p = (start != NULL) ? strpbrk(start, delim) : NULL;
|
||||||
|
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
*stringp = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*p = '\0';
|
||||||
|
*stringp = p + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void init_cars(void)
|
||||||
|
{
|
||||||
|
memset(s_cars, 0, sizeof s_cars);
|
||||||
|
|
||||||
|
FILE *f = fopen("cars.txt", "r");
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
struct car *car = s_cars;
|
||||||
|
|
||||||
|
while (!feof(f))
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
if (fgets(buf, sizeof buf, f) <= 0) break;
|
||||||
|
if (*buf == '#') continue;
|
||||||
|
|
||||||
|
char *bufp = buf, *p;
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
strncpy(car->tag, p, sizeof car->tag);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
strncpy(car->base, p, sizeof car->base);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
strncpy(car->cls, p, sizeof car->cls);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->intake = strtol(p, NULL, 10);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->weight = strtol(p, NULL, 10);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
strncpy(car->name, p, sizeof car->name);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->maxrpm = strtof(p, NULL);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->maxfuel = strtof(p, NULL);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->maxboost = strtof(p, NULL);
|
||||||
|
p = strsep(&bufp, ",");
|
||||||
|
car->maxspeed = strtof(p, NULL);
|
||||||
|
|
||||||
|
car++;
|
||||||
|
|
||||||
|
if (car - s_cars == MAX_CARS) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_car(const char tag[4], struct car *car)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_CARS; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(s_cars[i].tag, tag))
|
||||||
|
{
|
||||||
|
if (car != NULL) memcpy(car, &s_cars[i], sizeof *car);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *find_car(const char tag[4], int intake, int weight)
|
||||||
|
{
|
||||||
|
const struct car *best = NULL;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_CARS; i++)
|
||||||
|
{
|
||||||
|
const struct car *car = s_cars + i;
|
||||||
|
if (!strcmp(car->base, tag))
|
||||||
|
{
|
||||||
|
if (car->intake <= intake && car->weight <= weight)
|
||||||
|
{
|
||||||
|
if (best == NULL || (car->intake >= best->intake && car->weight >= best->weight))
|
||||||
|
{
|
||||||
|
best = s_cars + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_car(int carnum, float dist, float time, float cons)
|
||||||
|
{
|
||||||
|
s_cars[carnum].dist += dist;
|
||||||
|
s_cars[carnum].time += time;
|
||||||
|
s_cars[carnum].cons += cons;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef CARS_H
|
||||||
|
#define CARS_H
|
||||||
|
|
||||||
|
#define MAX_CARS 60
|
||||||
|
|
||||||
|
struct car
|
||||||
|
{
|
||||||
|
char tag[4];
|
||||||
|
char base[4];
|
||||||
|
char cls[20];
|
||||||
|
char name[20];
|
||||||
|
float maxrpm;
|
||||||
|
float maxfuel;
|
||||||
|
float maxboost;
|
||||||
|
float maxspeed;
|
||||||
|
int intake;
|
||||||
|
int weight;
|
||||||
|
|
||||||
|
float dist;
|
||||||
|
float time;
|
||||||
|
float cons;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct car s_cars[MAX_CARS];
|
||||||
|
|
||||||
|
void init_cars(void);
|
||||||
|
int get_car(const char tag[4], struct car *car);
|
||||||
|
const char *find_car(const char tag[4], int intake, int weight);
|
||||||
|
void update_car(int carnum, float dist, float time, float cons);
|
||||||
|
|
||||||
|
#endif /* CARS_H */
|
|
@ -0,0 +1,45 @@
|
||||||
|
UF1,UF1,STD,0,0,UF 1000,6983.477051,35.0,0.0,100
|
||||||
|
XFG,XFG,STD,0,0,XF GTI,7978.992188,45.0,0.0,120
|
||||||
|
XRG,XRG,STD,0,0,XR GT,6980.518066,65.0,0.0,130
|
||||||
|
LX4,LX4,TBO,0,0,LX4,8974.058594,40.0,0.0,130
|
||||||
|
LX6,LX6,LRF,0,0,LX6,8975.434570,40.0,0.0,140
|
||||||
|
RB4,RB4,TBO,0,0,RB4 GT,7480.626465,75.0,0.731383,150
|
||||||
|
FXO,FXO,TBO,0,0,FXO TURBO,7482.368164,75.0,0.733507,160
|
||||||
|
XRT,XRT,TBO,0,0,XR GT TURBO,7480.955078,75.0,0.830510,160
|
||||||
|
RAC,RAC,LRF,0,0,RACEABOUT,6985.558594,42.0,0.482715,160
|
||||||
|
FZ5,FZ5,LRF,0,0,FZ50,7971.276855,90.0,0.0,180
|
||||||
|
UFR,UFR,NGT,0,0,UF GTR,8978.926758,60.0,0.0,160
|
||||||
|
XFR,XFR,NGT,0,0,XF GTR,7979.339355,70.0,0.0,180
|
||||||
|
FXR,FXR,GTR,0,0,FXO GTR,7492.066895,100.0,1.759917,200
|
||||||
|
XRR,XRR,GTR,0,0,XR GTR,7492.067383,100.0,1.759917,200
|
||||||
|
FZR,FZR,GTR,0,0,FZ50 GTR,8474.996094,100.0,0.0,200
|
||||||
|
MRT,MRT,S-S,0,0,MRT5,12917.661133,20.0,0.138831,120
|
||||||
|
FBM,FBM,S-S,0,0,FORMULA BMW FB02,9179.628906,42.0,0.0,140
|
||||||
|
FOX,FOX,S-S,0,0,FORMULA XR,7481.007324,38.0,0.0,150
|
||||||
|
FO8,FO8,S-S,0,0,FORMULA V8,9476.702148,125.0,0.0,180
|
||||||
|
BF1,BF1,S-S,0,0,BMW SAUBER F1.06,19912.447266,95.0,0.0,220
|
||||||
|
FX2,FXR,GT2,23,0,FXO GT2,7492.066895,100.0,1.759917,200
|
||||||
|
XR2,XRR,GT2,24,0,XR GT2,7492.067383,100.0,1.759917,200
|
||||||
|
FZ2,FZR,GT2,20,0,FZ50 GT2,8474.996094,100.0,0.0,200
|
||||||
|
FX9,FXR,GT9,24,0,FXO GT9,7492.066895,100.0,1.759917,200
|
||||||
|
XR9,XRR,GT9,25,0,XR GT9,7492.067383,100.0,1.759917,200
|
||||||
|
FZ9,FZR,GT9,21,0,FZ50 GT9,8474.996094,100.0,0.0,200
|
||||||
|
FXP,FXR,GTP,10,10,FXO GTP,7492.066895,100.0,1.759917,200
|
||||||
|
XRP,XRR,GTP,10,20,XR GTP,7492.067383,100.0,1.759917,200
|
||||||
|
FZP,FZR,GTP,10,0,FZ50 GTP,8474.996094,100.0,0.0,200
|
||||||
|
FX3,FXR,GT3,32,0,FXO GT3,7492.066895,100.0,1.759917,200
|
||||||
|
XR3,XRR,GT3,34,0,XR GT3,7492.067383,100.0,1.759917,200
|
||||||
|
FZ3,FZR,GT3,28,0,FZ50 GT3,8474.996094,100.0,0.0,200
|
||||||
|
FX4,FXR,GT4,44,75,FXO GT4,7492.066895,100.0,1.759917,200
|
||||||
|
XR4,XRR,GT4,44,100,XR GT4,7492.067383,100.0,1.759917,200
|
||||||
|
FZ4,FZR,GT4,37,100,FZ50 GT4,8474.996094,100.0,0.0,200
|
||||||
|
UF4,UFR,GT4,12,50,UF GT4,8978.926758,60.0,0.0,160
|
||||||
|
XF4,XFR,GT4,10,60,XF GT4,7979.339355,70.0,0.0,180
|
||||||
|
UFB,UFR,BGT,45,0,UF-BR,8978.926758,60.0,0.0,160
|
||||||
|
XFB,XFR,BGT,43,0,XF-BR,7979.339355,70.0,0.0,180
|
||||||
|
RBS,RB4,S-series,4,60,RB4-S,7480.626465,75.0,0.731383,150
|
||||||
|
FXS,FXO,S-series,6,70,FXO-S,7482.368164,75.0,0.733507,160
|
||||||
|
XRS,XRT,S-series,5,60,XR GT-S,7480.955078,75.0,0.830510,160
|
||||||
|
FZS,FZ5,S-series,22,50,FZ50 S,7971.276855,90.0,0.0,180
|
||||||
|
FO,FXO,TWC,5,0,FO TURBO,7482.368164,75.0,0.733507,160
|
||||||
|
XT,XRT,TWC,1,0,XT TURBO,7480.955078,75.0,0.830510,160
|
|
@ -0,0 +1,182 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define MAXCONFIGLEN 64
|
||||||
|
|
||||||
|
struct configitem
|
||||||
|
{
|
||||||
|
char setting[MAXCONFIGLEN];
|
||||||
|
char value[MAXCONFIGLEN];
|
||||||
|
struct configitem *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
struct configitem *head;
|
||||||
|
} s_config;
|
||||||
|
|
||||||
|
static void config_free(void)
|
||||||
|
{
|
||||||
|
struct configitem *prev;
|
||||||
|
struct configitem *curr = s_config.head;
|
||||||
|
|
||||||
|
s_config.head = NULL;
|
||||||
|
|
||||||
|
while (curr != NULL)
|
||||||
|
{
|
||||||
|
prev = curr;
|
||||||
|
curr = curr->next;
|
||||||
|
free(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_set_string(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
struct configitem *item;
|
||||||
|
struct configitem **itemp = &s_config.head;
|
||||||
|
|
||||||
|
while (*itemp != NULL)
|
||||||
|
{
|
||||||
|
if (strcasecmp((*itemp)->setting, key) == 0)
|
||||||
|
{
|
||||||
|
item = *itemp;
|
||||||
|
if (value == NULL)
|
||||||
|
{
|
||||||
|
*itemp = item->next;
|
||||||
|
free(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(item->value, value, sizeof item->value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemp = &(*itemp)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == NULL) return;
|
||||||
|
|
||||||
|
*itemp = malloc(sizeof *item);
|
||||||
|
item = *itemp;
|
||||||
|
|
||||||
|
strncpy(item->setting, key, sizeof item->setting);
|
||||||
|
strncpy(item->value, value, sizeof item->value);
|
||||||
|
item->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_rehash(void)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(s_config.filename, "r");
|
||||||
|
if (f == NULL) return;
|
||||||
|
|
||||||
|
config_free();
|
||||||
|
|
||||||
|
while (!feof(f))
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
if (fgets(buf, sizeof buf, f) <= 0) break;
|
||||||
|
|
||||||
|
/* Ignore comments */
|
||||||
|
if (*buf == '#') continue;
|
||||||
|
|
||||||
|
char *n = strchr(buf, '=');
|
||||||
|
if (n == NULL) continue;
|
||||||
|
*n++ = '\0';
|
||||||
|
|
||||||
|
char *eol = strchr(n, '\n');
|
||||||
|
if (eol != NULL) *eol = '\0';
|
||||||
|
|
||||||
|
config_set_string(buf, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_write(void)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(s_config.filename, "w");
|
||||||
|
if (f == NULL) return;
|
||||||
|
|
||||||
|
struct configitem *curr;
|
||||||
|
for (curr = s_config.head; curr != NULL; curr = curr->next)
|
||||||
|
{
|
||||||
|
fprintf(f, "%s=%s\n", curr->setting, curr->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_init(const char *filename)
|
||||||
|
{
|
||||||
|
s_config.filename = filename;
|
||||||
|
s_config.head = NULL;
|
||||||
|
|
||||||
|
config_rehash();
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_deinit(void)
|
||||||
|
{
|
||||||
|
config_write();
|
||||||
|
config_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_set_int(const char *setting, int value)
|
||||||
|
{
|
||||||
|
char c[MAXCONFIGLEN];
|
||||||
|
snprintf(c, sizeof c, "%d", value);
|
||||||
|
config_set_string(setting, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_set_float(const char *setting, float value)
|
||||||
|
{
|
||||||
|
char c[MAXCONFIGLEN];
|
||||||
|
snprintf(c, sizeof c, "%f", value);
|
||||||
|
config_set_string(setting, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_get_string(const char *setting, char **value)
|
||||||
|
{
|
||||||
|
struct configitem *curr;
|
||||||
|
for (curr = s_config.head; curr != NULL; curr = curr->next)
|
||||||
|
{
|
||||||
|
if (strcasecmp(curr->setting, setting) == 0)
|
||||||
|
{
|
||||||
|
*value = curr->value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_get_int(const char *setting, int *value)
|
||||||
|
{
|
||||||
|
char *c, *endptr;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (!config_get_string(setting, &c)) return 0;
|
||||||
|
|
||||||
|
v = strtol(c, &endptr, 10);
|
||||||
|
if (*endptr != '\0') return 0;
|
||||||
|
|
||||||
|
*value = v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_get_float(const char *setting, float *value)
|
||||||
|
{
|
||||||
|
char *c, *endptr;
|
||||||
|
float v;
|
||||||
|
|
||||||
|
if (!config_get_string(setting, &c)) return 0;
|
||||||
|
|
||||||
|
v = strtof(c, &endptr);
|
||||||
|
if (*endptr != '\0') return 0;
|
||||||
|
|
||||||
|
*value = v;
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
void config_init(const char *filename);
|
||||||
|
void config_deinit(void);
|
||||||
|
|
||||||
|
void config_set_string(const char *setting, const char *value);
|
||||||
|
void config_set_int(const char *setting, int value);
|
||||||
|
void config_set_float(const char *setting, float value);
|
||||||
|
|
||||||
|
int config_get_string(const char *setting, char **value);
|
||||||
|
int config_get_int(const char *setting, int *value);
|
||||||
|
int config_get_float(const char *setting, float *value);
|
||||||
|
|
||||||
|
#endif /* CONFIG_H */
|
|
@ -0,0 +1,237 @@
|
||||||
|
#include <GL/glfw.h>
|
||||||
|
#include <FTGL/ftgl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "gauge.h"
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
|
void init_gauges(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawArc(float inner, float outer, float start_angle, float arc_angle, int num_segments)
|
||||||
|
{
|
||||||
|
num_segments = 72;
|
||||||
|
float theta = arc_angle / (float)(num_segments - 1);//theta is now calculated from the arc angle instead, the - 1 bit comes from the fact that the arc is open
|
||||||
|
|
||||||
|
float tangetial_factor = tanf(theta);
|
||||||
|
|
||||||
|
float radial_factor = cosf(theta);
|
||||||
|
|
||||||
|
float xi = inner * sinf(start_angle); //we now start at the start angle
|
||||||
|
float yi = inner * cosf(start_angle);
|
||||||
|
float xo = outer * sinf(start_angle); //we now start at the start angle
|
||||||
|
float yo = outer * cosf(start_angle);
|
||||||
|
|
||||||
|
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);//since the arc is not a closed curve, this is a strip now
|
||||||
|
int ii;
|
||||||
|
for(ii = 0; ii < num_segments; ii++)
|
||||||
|
{
|
||||||
|
glVertex3f(xi, yi, 0);
|
||||||
|
glVertex3f(xo, yo, 0);
|
||||||
|
|
||||||
|
float txi = -yi;
|
||||||
|
float tyi = xi;
|
||||||
|
|
||||||
|
xi += txi * tangetial_factor;
|
||||||
|
yi += tyi * tangetial_factor;
|
||||||
|
|
||||||
|
xi *= radial_factor;
|
||||||
|
yi *= radial_factor;
|
||||||
|
|
||||||
|
float txo = -yo;
|
||||||
|
float tyo = xo;
|
||||||
|
|
||||||
|
xo += txo * tangetial_factor;
|
||||||
|
yo += tyo * tangetial_factor;
|
||||||
|
|
||||||
|
xo *= radial_factor;
|
||||||
|
yo *= radial_factor;
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
float theta = 2 * 3.1415926 / (float)num_segments;
|
||||||
|
float c = cosf(theta);//precalculate the sine and cosine
|
||||||
|
float s = sinf(theta);
|
||||||
|
float t;
|
||||||
|
|
||||||
|
float x = r;//we start at angle = 0
|
||||||
|
float y = 0;
|
||||||
|
|
||||||
|
glBegin(GL_LINE_LOOP);
|
||||||
|
int ii;
|
||||||
|
for(ii = 0; ii < num_segments; ii++)
|
||||||
|
{
|
||||||
|
glVertex3f(x + cx, y + cy, 0);//output vertex
|
||||||
|
|
||||||
|
//apply the rotation matrix
|
||||||
|
t = x;
|
||||||
|
x = c * x - s * y;
|
||||||
|
y = s * t + c * y;
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
void drawDial(const struct gauge *gauge, float value, int style)
|
||||||
|
{
|
||||||
|
float range = gauge->rangemax - gauge->rangemin;
|
||||||
|
float angle = gauge->anglemax - gauge->anglemin;
|
||||||
|
float cura = (value - gauge->rangemin) / range * angle + gauge->anglemin;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (style)
|
||||||
|
{
|
||||||
|
case GT_NONE:
|
||||||
|
break;
|
||||||
|
case GT_BAR:
|
||||||
|
glColor4f(gauge->dial.r, gauge->dial.g, gauge->dial.b, gauge->dial.a);
|
||||||
|
DrawArc(gauge->majorstart, gauge->majorend, cura, cura - gauge->anglemin, 10);
|
||||||
|
break;
|
||||||
|
case GT_LINE2:
|
||||||
|
glColor4f(gauge->dial.r, gauge->dial.g, gauge->dial.b, gauge->dial.a);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(0, 0, 0);
|
||||||
|
glVertex3f(sin(cura), cos(cura), 0);
|
||||||
|
glEnd();
|
||||||
|
break;
|
||||||
|
case GT_LINE:
|
||||||
|
glColor4f(1.0, 0.0, 0.0, 1.0);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(sin(cura) * 0.8, cos(cura) * 0.8, 0);
|
||||||
|
glVertex3f(sin(cura), cos(cura), 0);
|
||||||
|
glEnd();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case GT_NEEDLE:
|
||||||
|
glColor4f(gauge->dial.r, gauge->dial.g, gauge->dial.b, gauge->dial.a);
|
||||||
|
glRotatef(cura * -180.0 / M_PI, 0.0, 0.0, 1.0);
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
|
//glVertex3f(-0.01, -0.04, 0.0);
|
||||||
|
//glVertex3f(0.01, -0.04, 0.0);
|
||||||
|
//glVertex3f(-0.02, 0, 0.0);
|
||||||
|
//glVertex3f(0.02, 0.0, 0.0);
|
||||||
|
//glVertex3f(-0.005, 1.0, 0.0);
|
||||||
|
//glVertex3f(0.005, 1.0, 0.0);
|
||||||
|
glVertex3f(-0.02, 0.2, 0.0);
|
||||||
|
glVertex3f(0.02, 0.2, 0.0);
|
||||||
|
glVertex3f(-0.005, 1.0, 0.0);
|
||||||
|
glVertex3f(0.005, 1.0, 0.0);
|
||||||
|
//glVertex3f(0.0, 1.0, 0.0);
|
||||||
|
glEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void draw_gauge(const struct gauge *gauge, float value, float red, FTGLfont *font, int gaugetype)
|
||||||
|
{
|
||||||
|
float range = gauge->rangemax - gauge->rangemin;
|
||||||
|
float angle = gauge->anglemax - gauge->anglemin;
|
||||||
|
float major;// = angle / (range / gauge->majorstep + 0.0);
|
||||||
|
float minor;// = angle / (range / gauge->minorstep + 0.0);
|
||||||
|
float cura;
|
||||||
|
float x1, y1, x2, y2;
|
||||||
|
int ii;
|
||||||
|
|
||||||
|
float majorstep = gauge->majorstep;
|
||||||
|
float minorstep = gauge->minorstep;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
major = angle / (range / majorstep + 0.0);
|
||||||
|
minor = angle / (range / minorstep + 0.0);
|
||||||
|
if (fabsf(major) < 12 * M_PI / 180.0) {
|
||||||
|
majorstep *= 2;
|
||||||
|
minorstep = majorstep / 2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < gauge->rangemin) value = gauge->rangemin;
|
||||||
|
if (value > gauge->rangemax * 2) value = gauge->rangemax * 2;
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glScalef(gauge->radius, gauge->radius, 1.0);
|
||||||
|
|
||||||
|
if (red > 0) {
|
||||||
|
glLineWidth(1.0);
|
||||||
|
glColor4f(0.8, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
cura = (red - gauge->rangemin) / range * angle + gauge->anglemin;
|
||||||
|
|
||||||
|
/*
|
||||||
|
float x1 = sin(cura) * gauge->majorstart;
|
||||||
|
float y1 = cos(cura) * gauge->majorstart;
|
||||||
|
float x2 = sin(cura) * gauge->majorend;
|
||||||
|
float y2 = cos(cura) * gauge->majorend;
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(x1, y1, 0);
|
||||||
|
glVertex3f(x2, y2, 0);
|
||||||
|
glEnd();*/
|
||||||
|
|
||||||
|
// printf("%f -> %f\n", cura, gauge->anglemax);
|
||||||
|
DrawArc(gauge->majorstart, gauge->majorend, gauge->anglemax, gauge->anglemax - cura, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
glLineWidth(gauge->minorwidth);
|
||||||
|
glColor4f(gauge->minor.r, gauge->minor.g, gauge->minor.b, gauge->minor.a);
|
||||||
|
// for (cura = gauge->anglemin; cura < gauge->anglemax; cura += minor)
|
||||||
|
for (ii = 0; ii <= range / minorstep; ii++)
|
||||||
|
{
|
||||||
|
if (red > 0 && ii * minorstep >= red) {
|
||||||
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
float cura = gauge->anglemin + ii * minor;
|
||||||
|
x1 = sin(cura) * gauge->minorstart;
|
||||||
|
y1 = cos(cura) * gauge->minorstart;
|
||||||
|
x2 = sin(cura) * gauge->minorend;
|
||||||
|
y2 = cos(cura) * gauge->minorend;
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(x1, y1, 0);
|
||||||
|
glVertex3f(x2, y2, 0);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
glLineWidth(gauge->majorwidth);
|
||||||
|
glColor4f(gauge->major.r, gauge->major.g, gauge->major.b, gauge->major.a);
|
||||||
|
// for (cura = gauge->anglemin; cura < gauge->anglemax; cura += major)
|
||||||
|
for (ii = 0; ii <= range / majorstep; ii++)
|
||||||
|
{
|
||||||
|
if (red > 0 && ii * majorstep > red) {
|
||||||
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
float cura = gauge->anglemin + ii * major;
|
||||||
|
x1 = sin(cura) * gauge->majorstart;
|
||||||
|
y1 = cos(cura) * gauge->majorstart;
|
||||||
|
x2 = sin(cura) * gauge->majorend;
|
||||||
|
y2 = cos(cura) * gauge->majorend;
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(x1, y1, 0);
|
||||||
|
glVertex3f(x2, y2, 0);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
char text[10];
|
||||||
|
if (gauge->majorstep > 100) {
|
||||||
|
snprintf(text, sizeof text, "%d", (int)(ii * majorstep / 1000 + gauge->rangemin));
|
||||||
|
} else {
|
||||||
|
if (range == 1) {
|
||||||
|
snprintf(text, sizeof text, ii == 0 ? "E" : ii == 1 ? "½" : "F");
|
||||||
|
} else {
|
||||||
|
snprintf(text, sizeof text, "%d", (int)(ii * majorstep + gauge->rangemin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float scale = (gauge->majorend - gauge->majorstart) * 1.5;
|
||||||
|
float offs = gauge->majorstart - scale;
|
||||||
|
drawText(text, font, sin(cura) * offs, cos(cura) * offs, scale, scale, TA_CENTRE, TA_CENTRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawDial(gauge, value, gaugetype);
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef GAUGE_H
|
||||||
|
#define GAUGE_H
|
||||||
|
|
||||||
|
#include <FTGL/ftgl.h>
|
||||||
|
|
||||||
|
struct colour
|
||||||
|
{
|
||||||
|
float r, g, b, a;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OG_SHIFT 1 // key
|
||||||
|
#define OG_CTRL 2 // key
|
||||||
|
|
||||||
|
#define OG_TURBO 8192 // show turbo gauge
|
||||||
|
#define OG_KM 16384 // if not set - user prefers MILES
|
||||||
|
#define OG_BAR 32768 // if not set - user prefers PSI
|
||||||
|
|
||||||
|
// DL_x - bits for OutGaugePack DashLights and ShowLights
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DL_SHIFT, // bit 0 - shift light
|
||||||
|
DL_FULLBEAM, // bit 1 - full beam
|
||||||
|
DL_HANDBRAKE, // bit 2 - handbrake
|
||||||
|
DL_PITSPEED, // bit 3 - pit speed limiter
|
||||||
|
DL_TC, // bit 4 - TC active or switched off
|
||||||
|
DL_SIGNAL_L, // bit 5 - left turn signal
|
||||||
|
DL_SIGNAL_R, // bit 6 - right turn signal
|
||||||
|
DL_SIGNAL_ANY, // bit 7 - shared turn signal
|
||||||
|
DL_OILWARN, // bit 8 - oil pressure warning
|
||||||
|
DL_BATTERY, // bit 9 - battery warning
|
||||||
|
DL_ABS, // bit 10 - ABS active or switched off
|
||||||
|
DL_SPARE, // bit 11
|
||||||
|
DL_NUM
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gauge
|
||||||
|
{
|
||||||
|
char name[16];
|
||||||
|
float radius;
|
||||||
|
float anglemin;
|
||||||
|
float anglemax;
|
||||||
|
float rangemin;
|
||||||
|
float rangemax;
|
||||||
|
|
||||||
|
float majorstep;
|
||||||
|
float majorwidth;
|
||||||
|
float majorstart;
|
||||||
|
float majorend;
|
||||||
|
struct colour major;
|
||||||
|
|
||||||
|
float minorstep;
|
||||||
|
float minorwidth;
|
||||||
|
float minorstart;
|
||||||
|
float minorend;
|
||||||
|
struct colour minor;
|
||||||
|
|
||||||
|
struct colour dial;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GT_NONE,
|
||||||
|
GT_NEEDLE,
|
||||||
|
GT_LINE,
|
||||||
|
GT_LINE2,
|
||||||
|
GT_BAR,
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_gauges(void);
|
||||||
|
void drawDial(const struct gauge *gauge, float value, int style);
|
||||||
|
void draw_gauge(const struct gauge *gauge, float value, float red, FTGLfont *font, int gaugetype);
|
||||||
|
|
||||||
|
#endif /* GAUGE_H */
|
|
@ -0,0 +1,13 @@
|
||||||
|
# name, start angle, end angle
|
||||||
|
# major: r, g, b, a, width, start, end
|
||||||
|
# minor: r, g, b, a, width, start, end
|
||||||
|
# dial: r, g, b, a
|
||||||
|
regular,30.0,270.0
|
||||||
|
1.0,1.0,1.0,1.0,3.0,0.9,1.0
|
||||||
|
0.8,0.8,0.8,1.0,1.0,0.95,0.97
|
||||||
|
1.0,0.0,0.0,0.5
|
||||||
|
fuel,150.0,210.0
|
||||||
|
1.0,1.0,1.0,1.0,3.0,0.9,1.0
|
||||||
|
0.8,0.8,0.8,1.0,1.0,0.95,0.97
|
||||||
|
1.0,0.0,0.0,0.5
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
#ifndef GETTIME_H
|
||||||
|
#define GETTIME_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define CLOCK_REALTIME 0
|
||||||
|
#define CLOCK_MONOTONIC 0
|
||||||
|
|
||||||
|
static inline LARGE_INTEGER getFILETIMEoffset()
|
||||||
|
{
|
||||||
|
SYSTEMTIME s;
|
||||||
|
FILETIME f;
|
||||||
|
LARGE_INTEGER t;
|
||||||
|
|
||||||
|
s.wYear = 1970;
|
||||||
|
s.wMonth = 1;
|
||||||
|
s.wDay = 1;
|
||||||
|
s.wHour = 0;
|
||||||
|
s.wMinute = 0;
|
||||||
|
s.wSecond = 0;
|
||||||
|
s.wMilliseconds = 0;
|
||||||
|
SystemTimeToFileTime(&s, &f);
|
||||||
|
t.QuadPart = f.dwHighDateTime;
|
||||||
|
t.QuadPart <<= 32;
|
||||||
|
t.QuadPart |= f.dwLowDateTime;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int clock_gettime(int X, struct timespec *ts)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER t;
|
||||||
|
FILETIME f;
|
||||||
|
double microseconds;
|
||||||
|
static LARGE_INTEGER offset;
|
||||||
|
static double frequencyToMicroseconds;
|
||||||
|
static int initialized = 0;
|
||||||
|
static BOOL usePerformanceCounter = 0;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
LARGE_INTEGER performanceFrequency;
|
||||||
|
initialized = 1;
|
||||||
|
usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
|
||||||
|
if (usePerformanceCounter) {
|
||||||
|
QueryPerformanceCounter(&offset);
|
||||||
|
frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
|
||||||
|
} else {
|
||||||
|
offset = getFILETIMEoffset();
|
||||||
|
frequencyToMicroseconds = 10.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (usePerformanceCounter) {
|
||||||
|
QueryPerformanceCounter(&t);
|
||||||
|
} else {
|
||||||
|
GetSystemTimeAsFileTime(&f);
|
||||||
|
t.QuadPart = f.dwHighDateTime;
|
||||||
|
t.QuadPart <<= 32;
|
||||||
|
t.QuadPart |= f.dwLowDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.QuadPart -= offset.QuadPart;
|
||||||
|
microseconds = (double)t.QuadPart / frequencyToMicroseconds;
|
||||||
|
t.QuadPart = microseconds;
|
||||||
|
ts->tv_sec = t.QuadPart / 1000000;
|
||||||
|
ts->tv_nsec = (t.QuadPart % 1000000) * 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline unsigned gettime(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,439 @@
|
||||||
|
//#define USE_IPV6
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#define MSG_NOSIGNAL 0
|
||||||
|
#else
|
||||||
|
//#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
//#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "cars.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "insim.h"
|
||||||
|
#include "network_worker.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "outgauge.h"
|
||||||
|
|
||||||
|
struct conninfo s_conninfo[256];
|
||||||
|
struct carinfo s_carinfo[256];
|
||||||
|
|
||||||
|
struct insim_buffer {
|
||||||
|
int state;
|
||||||
|
uint8_t buffer[65536];
|
||||||
|
int pos;
|
||||||
|
int offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct insim_buffer s_insim_tcp;
|
||||||
|
static struct insim_buffer s_insim_udp;
|
||||||
|
|
||||||
|
static struct queue_t *s_send_queue;
|
||||||
|
static uint8_t *s_send_packet;
|
||||||
|
static uint8_t s_send_pos;
|
||||||
|
|
||||||
|
void insim_queue(const void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
uint8_t *packet = malloc(size);
|
||||||
|
memcpy(packet, buffer, size);
|
||||||
|
queue_produce(s_send_queue, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
int insim_dequeue(int fd)
|
||||||
|
{
|
||||||
|
if (s_send_packet == NULL) queue_consume(s_send_queue, (void *)&s_send_packet);
|
||||||
|
|
||||||
|
if (s_send_packet != NULL)
|
||||||
|
{
|
||||||
|
ssize_t s = send(fd, (char *)s_send_packet + s_send_pos, s_send_packet[0] - s_send_pos, MSG_NOSIGNAL);
|
||||||
|
if (s >= 0) s_send_pos += s;
|
||||||
|
if (s_send_pos >= s_send_packet[0])
|
||||||
|
{
|
||||||
|
free(s_send_packet);
|
||||||
|
s_send_packet = 0;
|
||||||
|
s_send_pos = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_request_info(void)
|
||||||
|
{
|
||||||
|
memset(s_conninfo, 0, sizeof s_conninfo);
|
||||||
|
memset(s_carinfo, 0, sizeof s_carinfo);
|
||||||
|
|
||||||
|
struct IS_TINY p;
|
||||||
|
p.Size = sizeof p;
|
||||||
|
p.Type = ISP_TINY;
|
||||||
|
p.ReqI = 2;
|
||||||
|
p.SubT = TINY_NCN;
|
||||||
|
insim_queue(&p, sizeof p);
|
||||||
|
|
||||||
|
p.Size = sizeof p;
|
||||||
|
p.Type = ISP_TINY;
|
||||||
|
p.ReqI = 3;
|
||||||
|
p.SubT = TINY_NPL;
|
||||||
|
insim_queue(&p, sizeof p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_tiny(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_TINY *p = buffer;
|
||||||
|
switch (p->SubT)
|
||||||
|
{
|
||||||
|
case TINY_NONE:
|
||||||
|
{
|
||||||
|
struct IS_TINY r;
|
||||||
|
r.Size = sizeof r;
|
||||||
|
r.Type = ISP_TINY;
|
||||||
|
r.ReqI = 0;
|
||||||
|
r.SubT = TINY_NONE;
|
||||||
|
insim_queue(&r, sizeof r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ltc_duty(const char *uname, int state)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
if (!strcmp(uname, s_conninfo[i].uname)) {
|
||||||
|
s_conninfo[i].state = state;
|
||||||
|
printf("Player %s state %d\n", uname, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_ism(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_ISM *p = buffer;
|
||||||
|
|
||||||
|
printf("Joined %s\n", p->HName);
|
||||||
|
insim_request_info();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_mso(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
const struct IS_MSO *p = buffer;
|
||||||
|
printf("%d %d %d %d %s\n", p->UCID, p->PLID, p->UserType, p->TextStart, p->Msg);
|
||||||
|
|
||||||
|
char buf[128], *bufp = buf;
|
||||||
|
strncpy(buf, p->Msg, sizeof buf);
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
char *name = NULL;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
char *a = strsep(&bufp, " ");
|
||||||
|
if (a == NULL) return;
|
||||||
|
if (i == 0 && strcmp(a, "***")) return;
|
||||||
|
if (i == 1 && (!strcmp(a, "Officer") || !strcmp(a, "Cadet"))) state |= 1;
|
||||||
|
if (i == 2 && state == 1) name = a;
|
||||||
|
if (i == 3 && strcmp(a, "has")) return;
|
||||||
|
if (i == 4 && strcmp(a, "gone")) return;
|
||||||
|
if (i == 5 && !strcmp(a, "onduty")) ltc_duty(name, 1);
|
||||||
|
if (i == 5 && !strcmp(a, "offduty")) ltc_duty(name, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_ncn(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_NCN *p = buffer;
|
||||||
|
struct conninfo *c = s_conninfo + p->UCID;
|
||||||
|
memset(c, 0, sizeof *c);
|
||||||
|
c->active = 1;
|
||||||
|
strncpy(c->uname, p->UName, sizeof c->uname);
|
||||||
|
strncpy(c->pname, p->PName, sizeof c->pname);
|
||||||
|
c->admin = p->Admin;
|
||||||
|
|
||||||
|
printf("Know about connection %s (%s) %d\n", c->pname, c->uname, c->admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_cnl(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_CNL *p = buffer;
|
||||||
|
struct conninfo *c = s_conninfo + p->UCID;
|
||||||
|
c->active = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_cpr(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_CPR *p = buffer;
|
||||||
|
struct conninfo *c = s_conninfo + p->UCID;
|
||||||
|
if (c->active == 0) fprintf(stderr, "Rename of inactive connection\n");
|
||||||
|
strncpy(c->pname, p->PName, sizeof c->pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_npl(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_NPL *p = buffer;
|
||||||
|
struct carinfo *car = s_carinfo + p->PLID;
|
||||||
|
memset(car, 0, sizeof *car);
|
||||||
|
car->active = 1;
|
||||||
|
//strncpy(car->cname, p->CName, sizeof car->cname);
|
||||||
|
car->ucid = p->UCID;
|
||||||
|
car->mass = p->H_Mass;
|
||||||
|
car->intake = p->H_TRes;
|
||||||
|
|
||||||
|
strncpy(car->cname, find_car(p->CName, car->intake, car->mass), sizeof car->cname);
|
||||||
|
|
||||||
|
printf("Car %d joined (%s) - %s\n", p->PLID, car->cname, s_conninfo[car->ucid].uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_pll(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_PLL *p = buffer;
|
||||||
|
struct carinfo *car = s_carinfo + p->PLID;
|
||||||
|
car->active = 0;
|
||||||
|
printf("Car %d left - %s\n", p->PLID, s_conninfo[car->ucid].uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_handle_mci(int fd, const void *buffer)
|
||||||
|
{
|
||||||
|
const struct IS_MCI *p = buffer;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < p->NumC; i++)
|
||||||
|
{
|
||||||
|
const struct CompCar *c = p->Info + i;
|
||||||
|
struct carinfo *car = s_carinfo + c->PLID;
|
||||||
|
|
||||||
|
car->position = c->Position;
|
||||||
|
car->info = c->Info;
|
||||||
|
car->sp3 = c->Sp3;
|
||||||
|
car->x = c->X;
|
||||||
|
car->y = c->Y;
|
||||||
|
car->z = c->Z;
|
||||||
|
car->speed = c->Speed;
|
||||||
|
car->direction = c->Direction;
|
||||||
|
car->heading = c->Heading;
|
||||||
|
car->angvel = c->AngVel;
|
||||||
|
|
||||||
|
car->hist_x[car->hist] = c->X;
|
||||||
|
car->hist_y[car->hist] = c->Y;
|
||||||
|
car->hist_s[car->hist] = c->Speed;
|
||||||
|
car->hist = (car->hist + 1) % CARPATHSIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*insim_handler)(int fd, const void *buffer);
|
||||||
|
|
||||||
|
static const insim_handler s_handlers[] =
|
||||||
|
{
|
||||||
|
NULL, // ISP_NONE // 0 : not used
|
||||||
|
NULL, // ISP_ISI // 1 - instruction : insim initialise
|
||||||
|
NULL, // ISP_VER // 2 - info : version info
|
||||||
|
insim_handle_tiny, // ISP_TINY // 3 - both ways : multi purpose
|
||||||
|
NULL, // ISP_SMALL // 4 - both ways : multi purpose
|
||||||
|
NULL, // ISP_STA // 5 - info : state info
|
||||||
|
NULL, // ISP_SCH // 6 - instruction : single character
|
||||||
|
NULL, // ISP_SFP // 7 - instruction : state flags pack
|
||||||
|
NULL, // ISP_SCC // 8 - instruction : set car camera
|
||||||
|
NULL, // ISP_CPP // 9 - both ways : cam pos pack
|
||||||
|
insim_handle_ism, // ISP_ISM // 10 - info : start multiplayer
|
||||||
|
insim_handle_mso, // ISP_MSO // 11 - info : message out
|
||||||
|
NULL, // ISP_III // 12 - info : hidden /i message
|
||||||
|
NULL, // ISP_MST // 13 - instruction : type message or /command
|
||||||
|
NULL, // ISP_MTC // 14 - instruction : message to a connection
|
||||||
|
NULL, // ISP_MOD // 15 - instruction : set screen mode
|
||||||
|
NULL, // ISP_VTN // 16 - info : vote notification
|
||||||
|
NULL, // ISP_RST // 17 - info : race start
|
||||||
|
insim_handle_ncn, // ISP_NCN // 18 - info : new connection
|
||||||
|
insim_handle_cnl, // ISP_CNL // 19 - info : connection left
|
||||||
|
insim_handle_cpr, // ISP_CPR // 20 - info : connection renamed
|
||||||
|
insim_handle_npl, // ISP_NPL // 21 - info : new player (joined race)
|
||||||
|
NULL, // ISP_PLP // 22 - info : player pit (keeps slot in race)
|
||||||
|
insim_handle_pll, // ISP_PLL // 23 - info : player leave (spectate - loses slot)
|
||||||
|
NULL, // ISP_LAP // 24 - info : lap time
|
||||||
|
NULL, // ISP_SPX // 25 - info : split x time
|
||||||
|
NULL, // ISP_PIT // 26 - info : pit stop start
|
||||||
|
NULL, // ISP_PSF // 27 - info : pit stop finish
|
||||||
|
NULL, // ISP_PLA // 28 - info : pit lane enter / leave
|
||||||
|
NULL, // ISP_CCH // 29 - info : camera changed
|
||||||
|
NULL, // ISP_PEN // 30 - info : penalty given or cleared
|
||||||
|
NULL, // ISP_TOC // 31 - info : take over car
|
||||||
|
NULL, // ISP_FLG // 32 - info : flag (yellow or blue)
|
||||||
|
NULL, // ISP_PFL // 33 - info : player flags (help flags)
|
||||||
|
NULL, // ISP_FIN // 34 - info : finished race
|
||||||
|
NULL, // ISP_RES // 35 - info : result confirmed
|
||||||
|
NULL, // ISP_REO // 36 - both ways : reorder (info or instruction)
|
||||||
|
NULL, // ISP_NLP // 37 - info : node and lap packet
|
||||||
|
insim_handle_mci, // ISP_MCI // 38 - info : multi car info
|
||||||
|
NULL, // ISP_MSX // 39 - instruction : type message
|
||||||
|
NULL, // ISP_MSL // 40 - instruction : message to local computer
|
||||||
|
NULL, // ISP_CRS // 41 - info : car reset
|
||||||
|
NULL, // ISP_BFN // 42 - both ways : delete buttons / receive button requests
|
||||||
|
NULL, // ISP_AXI // 43 - info : autocross layout information
|
||||||
|
NULL, // ISP_AXO // 44 - info : hit an autocross object
|
||||||
|
NULL, // ISP_BTN // 45 - instruction : show a button on local or remote screen
|
||||||
|
NULL, // ISP_BTC // 46 - info : sent when a user clicks a button
|
||||||
|
NULL, // ISP_BTT // 47 - info : sent after typing into a button
|
||||||
|
NULL, // ISP_RIP // 48 - both ways : replay information packet
|
||||||
|
NULL, // ISP_SSH // 49 - both ways : screenshot
|
||||||
|
NULL, // ISP_CON // 50 - info : contact between cars (collision report)
|
||||||
|
NULL, // ISP_OBH // 51 - info : contact car + object (collision report)
|
||||||
|
NULL, // ISP_HLV // 52 - info : report incidents that would violate HLVC
|
||||||
|
NULL, // ISP_PLC // 53 - instruction : player cars
|
||||||
|
NULL, // ISP_AXM // 54 - both ways : autocross multiple objects
|
||||||
|
NULL, // ISP_ACR // 55 - info : admin command report
|
||||||
|
};
|
||||||
|
|
||||||
|
void insim_handle(int fd, const uint8_t *buffer)
|
||||||
|
{
|
||||||
|
uint8_t type = buffer[1];
|
||||||
|
if (type <= ISP_ACR && s_handlers[type] != NULL)
|
||||||
|
{
|
||||||
|
s_handlers[type](fd, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int insim_recv(int fd, int can_write, int can_read, void *arg)
|
||||||
|
{
|
||||||
|
struct insim_buffer *buffer = arg;
|
||||||
|
|
||||||
|
if (can_write && buffer->state == 1)
|
||||||
|
{
|
||||||
|
insim_dequeue(fd);
|
||||||
|
}
|
||||||
|
if (can_read)
|
||||||
|
{
|
||||||
|
if (buffer->pos >= sizeof buffer->buffer)
|
||||||
|
{
|
||||||
|
printf("buffer full\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssize_t res = recv(fd, (char *)buffer->buffer + buffer->pos, sizeof buffer->buffer - buffer->pos, 0);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
if (WSAGetLastError() == WSAECONNRESET)
|
||||||
|
#else
|
||||||
|
if (errno == ECONNRESET)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
printf("connreset\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef WIN32
|
||||||
|
else if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||||
|
#else
|
||||||
|
else if (errno != EWOULDBLOCK && errno != EAGAIN)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
printf("hmm: %s\n", strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res == 0)
|
||||||
|
{
|
||||||
|
printf("nothing received\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->pos += res;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
uint8_t size = buffer->buffer[buffer->offset];
|
||||||
|
if (size % 4 != 0)
|
||||||
|
{
|
||||||
|
printf("invalid packet, size is %d\n", size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer->pos - buffer->offset >= size)
|
||||||
|
{
|
||||||
|
insim_handle(fd, buffer->buffer + buffer->offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer->pos - buffer->offset > size)
|
||||||
|
{
|
||||||
|
buffer->offset += size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer->pos = 0;
|
||||||
|
buffer->offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
#ifdef USE_IPV6
|
||||||
|
static struct sockaddr_in6 serv_addr;
|
||||||
|
#else
|
||||||
|
static struct sockaddr_in serv_addr;
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int g_outgauge_port;
|
||||||
|
extern int g_insim_port;
|
||||||
|
|
||||||
|
static void insim_connect(int fd, void *arg)
|
||||||
|
{
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_insim_tcp.state = 1;
|
||||||
|
s_insim_tcp.pos = 0;
|
||||||
|
s_insim_tcp.offset = 0;
|
||||||
|
register_socket(fd, SM_READ | SM_WRITE, &insim_recv, &s_insim_tcp);
|
||||||
|
|
||||||
|
fd = network_listen(g_insim_port + 1, 0);
|
||||||
|
s_insim_udp.state = 2;
|
||||||
|
s_insim_udp.pos = 0;
|
||||||
|
s_insim_udp.offset = 0;
|
||||||
|
register_socket(fd, SM_READ, &insim_recv, &s_insim_udp);
|
||||||
|
|
||||||
|
struct IS_ISI p;
|
||||||
|
memset(&p, 0, sizeof p);
|
||||||
|
p.Size = sizeof p;
|
||||||
|
p.Type = ISP_ISI;
|
||||||
|
p.ReqI = 0;
|
||||||
|
p.Zero = 0;
|
||||||
|
p.UDPPort = g_outgauge_port; //g_insim_port + 1;
|
||||||
|
p.Flags = ISF_LOCAL;
|
||||||
|
p.Sp0 = 0;
|
||||||
|
p.Prefix = 0;
|
||||||
|
p.Interval = 100;
|
||||||
|
snprintf(p.Admin, sizeof p.Admin, "");
|
||||||
|
snprintf(p.IName, sizeof p.IName, "LFS Dashboard");
|
||||||
|
insim_queue(&p, sizeof p);
|
||||||
|
|
||||||
|
struct IS_SMALL s;
|
||||||
|
s.Size = sizeof s;
|
||||||
|
s.Type = ISP_SMALL;
|
||||||
|
s.ReqI = 1;
|
||||||
|
s.SubT = SMALL_SSG;
|
||||||
|
s.UVal = 1;
|
||||||
|
insim_queue(&s, sizeof s);
|
||||||
|
|
||||||
|
insim_request_info();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insim_init(const char *hostname, int port)
|
||||||
|
{
|
||||||
|
s_insim_tcp.state = 0;
|
||||||
|
s_insim_udp.state = 0;
|
||||||
|
s_send_queue = queue_new();
|
||||||
|
network_connect(hostname, port, &insim_connect, NULL);
|
||||||
|
}
|
|
@ -0,0 +1,994 @@
|
||||||
|
#include <GL/glfw.h>
|
||||||
|
#include <FTGL/ftgl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "outgauge.h"
|
||||||
|
#include "cars.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "gauge.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "audio.h"
|
||||||
|
#include "text.h"
|
||||||
|
#include "insim.h"
|
||||||
|
#include "network_worker.h"
|
||||||
|
|
||||||
|
static GLuint s_symbols[1];
|
||||||
|
|
||||||
|
struct sympos {
|
||||||
|
float a, b, c, d;
|
||||||
|
int flag;
|
||||||
|
struct colour off;
|
||||||
|
struct colour on;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
DL_SHIFT, // bit 0 - shift light
|
||||||
|
DL_FULLBEAM, // bit 1 - full beam
|
||||||
|
DL_HANDBRAKE, // bit 2 - handbrake
|
||||||
|
DL_PITSPEED, // bit 3 - pit speed limiter
|
||||||
|
DL_TC, // bit 4 - TC active or switched off
|
||||||
|
DL_SIGNAL_L, // bit 5 - left turn signal
|
||||||
|
DL_SIGNAL_R, // bit 6 - right turn signal
|
||||||
|
DL_SIGNAL_ANY, // bit 7 - shared turn signal
|
||||||
|
DL_OILWARN, // bit 8 - oil pressure warning
|
||||||
|
DL_BATTERY, // bit 9 - battery warning
|
||||||
|
DL_ABS, // bit 10 - ABS active or switched off
|
||||||
|
DL_SPARE, // bit 11
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int dl_to_sympos[] = {
|
||||||
|
18, 0, 12, 17, 11, 15, 16, 2, 10, 9, 14, 13
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sympos s_sympos[] = {
|
||||||
|
{ 0.0, 0.0, 0.125, 0.125, (1 << DL_FULLBEAM), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 0.15, 1.0, 1.0 } },
|
||||||
|
{ 0.125, 0.0, 0.25, 0.125, 0, { 0.1, 0.1, 0.1, 1.0 }, { 0.5, 0.5, 1.0, 1.0 } },
|
||||||
|
{ 0.25, 0.0, 0.375, 0.125, (1 << DL_SIGNAL_ANY), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 1.0, 0.15, 1.0 } },
|
||||||
|
{ 0.375, 0.0, 0.5, 0.125, 0, { 0.1, 0.1, 0.1, 1.0 }, { 0.5, 0.5, 1.0, 1.0 } },
|
||||||
|
|
||||||
|
{ 0.0, 0.125, 0.125, 0.25, 0, { 1.0, 1.0, 1.0, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
{ 0.125, 0.125, 0.25, 0.25, 0, { 1.0, 1.0, 1.0, 1.0 }, { 0.15, 0.15, 1.0, 1.0 } },
|
||||||
|
{ 0.25, 0.125, 0.375, 0.25, 0, { 1.0, 1.0, 1.0, 1.0 }, { 0.15, 0.15, 1.0, 1.0 } },
|
||||||
|
{ 0.375, 0.125, 0.5, 0.25, 0, { 1.0, 1.0, 1.0, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
{ 0.5, 0.125, 0.625, 0.25, 0, { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 0.15, 0.15, 1.0 } },
|
||||||
|
|
||||||
|
{ 0.0, 0.25, 0.125, 0.375, (1 << DL_BATTERY), { 0.1, 0.1, 0.1, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
{ 0.125, 0.25, 0.25, 0.375, (1 << DL_OILWARN), { 0.1, 0.1, 0.1, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
{ 0.25, 0.25, 0.375, 0.375, (1 << DL_TC), { 0.1, 0.1, 0.1, 1.0 }, { 1.0, 0.5, 0.0, 1.0 } },
|
||||||
|
{ 0.375, 0.25, 0.5, 0.375, (1 << DL_HANDBRAKE), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 0.15, 1.0, 1.0 } },
|
||||||
|
{ 0.5, 0.25, 0.625, 0.375, 0, { 0.1, 0.1, 0.1, 1.0 }, { 0.25, 0.25, 1.0, 1.0 } },
|
||||||
|
{ 0.625, 0.25, 0.75, 0.375, (1 << DL_ABS), { 0.1, 0.1, 0.1, 1.0 }, { 1.0, 0.5, 0.0, 1.0 } },
|
||||||
|
|
||||||
|
{ 0.0, 0.375, 0.125, 0.5, (1 << DL_SIGNAL_L), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 1.0, 0.15, 1.0 } },
|
||||||
|
{ 0.125, 0.375, 0.25, 0.5, (1 << DL_SIGNAL_R), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 1.0, 0.15, 1.0 } },
|
||||||
|
{ 0.25, 0.375, 0.375, 0.5, (1 << DL_PITSPEED), { 0.1, 0.1, 0.1, 1.0 }, { 0.15, 1.0, 0.15, 1.0 } },
|
||||||
|
{ 0.375, 0.375, 0.5, 0.5, (1 << DL_SHIFT), { 0.1, 0.1, 0.1, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
|
||||||
|
{ 0.5, 0.0, 0.625, 0.125, 0, { 1.0, 0.5, 0.0, 1.0 }, { 1.0, 0.15, 0.15, 1.0 } },
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void drawSymbol(int symbol, int on, int map)
|
||||||
|
{
|
||||||
|
if (map) symbol = dl_to_sympos[symbol];
|
||||||
|
|
||||||
|
struct sympos *s = &s_sympos[symbol];
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, s_symbols[0]);
|
||||||
|
if (on) {
|
||||||
|
glColor4f(s->on.r, s->on.g, s->on.b, s->on.a);
|
||||||
|
} else {
|
||||||
|
glColor4f(s->off.r, s->off.g, s->off.b, s->off.a);
|
||||||
|
}
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glTexCoord2f(s->a, (on ? 0.5 : 1.0) - s->d);
|
||||||
|
glVertex3f(-0.5, -0.5, 0.0);
|
||||||
|
glTexCoord2f(s->c, (on ? 0.5 : 1.0) - s->d);
|
||||||
|
glVertex3f(0.5, -0.5, 0.0);
|
||||||
|
glTexCoord2f(s->c, (on ? 0.5 : 1.0) - s->b);
|
||||||
|
glVertex3f(0.5, 0.5, 0.0);
|
||||||
|
glTexCoord2f(s->a, (on ? 0.5 : 1.0) - s->b);
|
||||||
|
glVertex3f(-0.5, 0.5, 0.0);
|
||||||
|
glEnd();
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawSymbol2(int symbol, int on, int map, float x, float y, float xs, float ys)
|
||||||
|
{
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(x, y, 0.0f);
|
||||||
|
glScalef(xs, ys, 1.0f);
|
||||||
|
drawSymbol(symbol, on, map);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
int g_outgauge_port;
|
||||||
|
int g_insim_port;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_fade = 0;
|
||||||
|
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int fullscreen;
|
||||||
|
int volume;
|
||||||
|
int rpmleft;
|
||||||
|
int dofade;
|
||||||
|
char *insim_host;
|
||||||
|
|
||||||
|
float warn_fuel1;
|
||||||
|
float warn_fuel2;
|
||||||
|
|
||||||
|
config_init("lfsdash.txt");
|
||||||
|
if (!config_get_int("port", &g_outgauge_port)) g_outgauge_port = 4000;
|
||||||
|
if (!config_get_int("fullscreen", &fullscreen)) fullscreen = 0;
|
||||||
|
if (!config_get_int("width", &width)) width = 1024;
|
||||||
|
if (!config_get_int("height", &height)) height = 600;
|
||||||
|
if (!config_get_int("volume", &volume)) volume = 100;
|
||||||
|
if (!config_get_int("rpmleft", &rpmleft)) rpmleft = 0;
|
||||||
|
if (!config_get_int("fade", &dofade)) dofade = 1;
|
||||||
|
if (!config_get_string("insim_host", &insim_host)) insim_host = strdup("localhost");
|
||||||
|
if (!config_get_int("insim_port", &g_insim_port)) g_insim_port = 29999;
|
||||||
|
if (!config_get_float("warn_fuel1", &warn_fuel1)) warn_fuel1 = 0.05f;
|
||||||
|
if (!config_get_float("warn_fuel2", &warn_fuel2)) warn_fuel2 = 0.01f;
|
||||||
|
|
||||||
|
network_worker_init();
|
||||||
|
|
||||||
|
socket_init();
|
||||||
|
outgauge_init(g_outgauge_port);
|
||||||
|
insim_init(insim_host, g_insim_port);
|
||||||
|
init_cars();
|
||||||
|
|
||||||
|
glfwInit();
|
||||||
|
glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 1);
|
||||||
|
if (!glfwOpenWindow(width, height, 0, 0, 0, 0, 0, 0, fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOW))
|
||||||
|
{
|
||||||
|
glfwTerminate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSetWindowTitle("GL LFS Dashboard");
|
||||||
|
|
||||||
|
audio_init();
|
||||||
|
|
||||||
|
struct gauge speed;
|
||||||
|
speed.radius = 200.0;
|
||||||
|
speed.anglemin = (180 + 45) * M_PI / 180.0;
|
||||||
|
speed.anglemax = (180 + 315) * M_PI / 180.0;
|
||||||
|
speed.rangemin = 0;
|
||||||
|
speed.rangemax = 160.0;
|
||||||
|
speed.majorstep = 10.0;
|
||||||
|
speed.majorwidth = 3.0;
|
||||||
|
speed.majorstart = 0.9;
|
||||||
|
speed.majorend = 1.0;
|
||||||
|
speed.major.r = 0.15;
|
||||||
|
speed.major.g = 0.15;
|
||||||
|
speed.major.b = 1.0;
|
||||||
|
speed.major.a = 1.0;
|
||||||
|
speed.minorstep = 2.0;
|
||||||
|
speed.minorwidth = 1.0;
|
||||||
|
speed.minorstart = 0.95;
|
||||||
|
speed.minorend = 1.0;
|
||||||
|
speed.minor.r = 0.15;
|
||||||
|
speed.minor.g = 0.15;
|
||||||
|
speed.minor.b = 1.0;
|
||||||
|
speed.minor.a = 1.0;
|
||||||
|
speed.dial.r = 1.0;
|
||||||
|
speed.dial.g = 1.0;
|
||||||
|
speed.dial.b = 1.0;
|
||||||
|
speed.dial.a = 0.8;
|
||||||
|
|
||||||
|
struct gauge speed2;
|
||||||
|
speed2.radius = 200.0;
|
||||||
|
speed2.anglemin = (180 + 45) * M_PI / 180.0;
|
||||||
|
speed2.anglemax = (180 + 315) * M_PI / 180.0;
|
||||||
|
speed2.rangemin = 0;
|
||||||
|
speed2.rangemax = 160.0;
|
||||||
|
speed2.majorstep = 10.0;
|
||||||
|
speed2.majorwidth = 1.5;
|
||||||
|
speed2.majorstart = 0.92;
|
||||||
|
speed2.majorend = 1.0;
|
||||||
|
speed2.major.r = 0.15;
|
||||||
|
speed2.major.g = 0.15;
|
||||||
|
speed2.major.b = 1.0;
|
||||||
|
speed2.major.a = 0.5;
|
||||||
|
speed2.minorstep = 2.0;
|
||||||
|
speed2.minorwidth = 1.0;
|
||||||
|
speed2.minorstart = 0.95;
|
||||||
|
speed2.minorend = 1.0;
|
||||||
|
speed2.minor.r = 0.15;
|
||||||
|
speed2.minor.g = 0.15;
|
||||||
|
speed2.minor.b = 1.0;
|
||||||
|
speed2.minor.a = 0.5;
|
||||||
|
speed2.dial.r = 1.0;
|
||||||
|
speed2.dial.g = 1.0;
|
||||||
|
speed2.dial.b = 1.0;
|
||||||
|
speed2.dial.a = 0.0;
|
||||||
|
|
||||||
|
struct gauge rpm;
|
||||||
|
rpm.radius = 200.0;
|
||||||
|
rpm.anglemin = (180 + 45) * M_PI / 180.0;
|
||||||
|
rpm.anglemax = (180 + 315) * M_PI / 180.0;
|
||||||
|
rpm.rangemin = 0;
|
||||||
|
rpm.rangemax = 10000.0;
|
||||||
|
rpm.majorstep = 1000.0;
|
||||||
|
rpm.majorwidth = 3.0;
|
||||||
|
rpm.majorstart = 0.9;
|
||||||
|
rpm.majorend = 1.0;
|
||||||
|
rpm.major.r = 0.15;
|
||||||
|
rpm.major.g = 0.15;
|
||||||
|
rpm.major.b = 1.0;
|
||||||
|
rpm.major.a = 1.0;
|
||||||
|
rpm.minorstep = 200.0;
|
||||||
|
rpm.minorwidth = 1.0;
|
||||||
|
rpm.minorstart = 0.95;
|
||||||
|
rpm.minorend = 1.0;
|
||||||
|
rpm.minor.r = 0.15;
|
||||||
|
rpm.minor.g = 0.15;
|
||||||
|
rpm.minor.b = 1.0;
|
||||||
|
rpm.minor.a = 1.0;
|
||||||
|
rpm.dial.r = 1.0;
|
||||||
|
rpm.dial.g = 1.0;
|
||||||
|
rpm.dial.b = 1.0;
|
||||||
|
rpm.dial.a = 0.8;
|
||||||
|
|
||||||
|
struct gauge mpg;
|
||||||
|
mpg.radius = 160.0;
|
||||||
|
mpg.anglemin = 120 * M_PI / 180.0;
|
||||||
|
mpg.anglemax = 60 * M_PI / 180.0;
|
||||||
|
mpg.rangemin = 0;
|
||||||
|
mpg.rangemax = 100.0;
|
||||||
|
mpg.majorstep = 20.0;
|
||||||
|
mpg.majorwidth = 3.0;
|
||||||
|
mpg.majorstart = 0.95;
|
||||||
|
mpg.majorend = 1.0;
|
||||||
|
mpg.major.r = 0.15;
|
||||||
|
mpg.major.g = 0.15;
|
||||||
|
mpg.major.b = 1.0;
|
||||||
|
mpg.major.a = 1.0;
|
||||||
|
mpg.minorstep = 10.0;
|
||||||
|
mpg.minorwidth = 1.0;
|
||||||
|
mpg.minorstart = 0.95;
|
||||||
|
mpg.minorend = 1.0;
|
||||||
|
mpg.minor.r = 0.15;
|
||||||
|
mpg.minor.g = 0.15;
|
||||||
|
mpg.minor.b = 1.0;
|
||||||
|
mpg.minor.a = 1.0;
|
||||||
|
mpg.dial.r = 1.0;
|
||||||
|
mpg.dial.g = 1.0;
|
||||||
|
mpg.dial.b = 1.0;
|
||||||
|
mpg.dial.a = 0.8;
|
||||||
|
|
||||||
|
struct gauge boost;
|
||||||
|
boost.radius = 160.0;
|
||||||
|
boost.anglemin = 240 * M_PI / 180.0;
|
||||||
|
boost.anglemax = 300 * M_PI / 180.0;
|
||||||
|
boost.rangemin = 0.0;
|
||||||
|
boost.rangemax = 2.0;
|
||||||
|
boost.majorstep = 0.5;
|
||||||
|
boost.majorwidth = 2.0;
|
||||||
|
boost.majorstart = 0.95;
|
||||||
|
boost.majorend = 1.0;
|
||||||
|
boost.major.r = 0.15;
|
||||||
|
boost.major.g = 0.15;
|
||||||
|
boost.major.b = 1.0;
|
||||||
|
boost.major.a = 1.0;
|
||||||
|
boost.minorstep = 0.1;
|
||||||
|
boost.minorwidth = 1.0;
|
||||||
|
boost.minorstart = 0.95;
|
||||||
|
boost.minorend = 1.0;
|
||||||
|
boost.minor.r = 0.15;
|
||||||
|
boost.minor.g = 0.15;
|
||||||
|
boost.minor.b = 1.0;
|
||||||
|
boost.minor.a = 1.0;
|
||||||
|
boost.dial.r = 1.0;
|
||||||
|
boost.dial.g = 1.0;
|
||||||
|
boost.dial.b = 1.0;
|
||||||
|
boost.dial.a = 0.8;
|
||||||
|
|
||||||
|
struct gauge fuel;
|
||||||
|
fuel.radius = 160.0;
|
||||||
|
fuel.anglemin = 240 * M_PI / 180.0;
|
||||||
|
fuel.anglemax = 300 * M_PI / 180.0;
|
||||||
|
fuel.rangemin = 0.0;
|
||||||
|
fuel.rangemax = 1.0;
|
||||||
|
fuel.majorstep = 0.5;
|
||||||
|
fuel.majorwidth = 2.0;
|
||||||
|
fuel.majorstart = 0.95;
|
||||||
|
fuel.majorend = 1.0;
|
||||||
|
fuel.major.r = 0.15;
|
||||||
|
fuel.major.g = 0.15;
|
||||||
|
fuel.major.b = 1.0;
|
||||||
|
fuel.major.a = 1.0;
|
||||||
|
fuel.minorstep = 0.1;
|
||||||
|
fuel.minorwidth = 1.0;
|
||||||
|
fuel.minorstart = 0.95;
|
||||||
|
fuel.minorend = 1.0;
|
||||||
|
fuel.minor.r = 0.15;
|
||||||
|
fuel.minor.g = 0.15;
|
||||||
|
fuel.minor.b = 1.0;
|
||||||
|
fuel.minor.a = 1.0;
|
||||||
|
fuel.dial.r = 1.0;
|
||||||
|
fuel.dial.g = 1.0;
|
||||||
|
fuel.dial.b = 1.0;
|
||||||
|
fuel.dial.a = 0.8;
|
||||||
|
|
||||||
|
struct gauge oil;
|
||||||
|
oil.radius = 160.0;
|
||||||
|
oil.anglemin = 120 * M_PI / 180.0;
|
||||||
|
oil.anglemax = 60 * M_PI / 180.0;
|
||||||
|
oil.rangemin = 0.0;
|
||||||
|
oil.rangemax = 2.0;
|
||||||
|
oil.majorstep = 0.5;
|
||||||
|
oil.majorwidth = 2.0;
|
||||||
|
oil.majorstart = 0.95;
|
||||||
|
oil.majorend = 1.0;
|
||||||
|
oil.major.r = 0.15;
|
||||||
|
oil.major.g = 0.15;
|
||||||
|
oil.major.b = 1.0;
|
||||||
|
oil.major.a = 1.0;
|
||||||
|
oil.minorstep = 0.1;
|
||||||
|
oil.minorwidth = 1.0;
|
||||||
|
oil.minorstart = 0.95;
|
||||||
|
oil.minorend = 1.0;
|
||||||
|
oil.minor.r = 0.15;
|
||||||
|
oil.minor.g = 0.15;
|
||||||
|
oil.minor.b = 1.0;
|
||||||
|
oil.minor.a = 1.0;
|
||||||
|
oil.dial.r = 1.0;
|
||||||
|
oil.dial.g = 1.0;
|
||||||
|
oil.dial.b = 1.0;
|
||||||
|
oil.dial.a = 0.8;
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct gauge clock;
|
||||||
|
clock.radius = 60.0;
|
||||||
|
clock.anglemin = 0 * M_PI / 180.0;
|
||||||
|
clock.anglemax = 360 * M_PI / 180.0;
|
||||||
|
clock.rangemin = 0.0;
|
||||||
|
clock.rangemax = 12.0;
|
||||||
|
clock.majorstep = 1.0;
|
||||||
|
clock.majorwidth = 2.0;
|
||||||
|
clock.majorstart = 0.9;
|
||||||
|
clock.majorend = 1.0;
|
||||||
|
clock.major.r = 0.15;
|
||||||
|
clock.major.g = 0.15;
|
||||||
|
clock.major.b = 1.0;
|
||||||
|
clock.major.a = 1.0;
|
||||||
|
clock.minorstep = 0.25;
|
||||||
|
clock.minorwidth = 1.0;
|
||||||
|
clock.minorstart = 0.95;
|
||||||
|
clock.minorend = 1.0;
|
||||||
|
clock.minor.r = 0.15;
|
||||||
|
clock.minor.g = 0.15;
|
||||||
|
clock.minor.b = 1.0;
|
||||||
|
clock.minor.a = 1.0;
|
||||||
|
clock.dial.r = 1.0;
|
||||||
|
clock.dial.g = 1.0;
|
||||||
|
clock.dial.b = 1.0;
|
||||||
|
clock.dial.a = 0.8;
|
||||||
|
*/
|
||||||
|
char text[40];
|
||||||
|
|
||||||
|
//float last_fuel = 1.0;
|
||||||
|
//float consumption = 0.0;
|
||||||
|
double next = glfwGetTime();
|
||||||
|
double last_time = next;
|
||||||
|
struct car car;
|
||||||
|
|
||||||
|
int running = 1;
|
||||||
|
|
||||||
|
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
//void *(*start_routine)(void*), void *arg);
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
pthread_create(&thread, NULL, &socket_run, NULL);
|
||||||
|
|
||||||
|
FTGLfont *font = ftglCreateTextureFont("arialbd.ttf");//"Arial.ttf");//"/usr/share/fonts/truetype/msttcorefonts/Arial.ttf");
|
||||||
|
//DS_DIGII
|
||||||
|
if (!font) return 0;
|
||||||
|
|
||||||
|
ftglSetFontFaceSize(font, TEXT_SIZE, TEXT_SIZE);
|
||||||
|
|
||||||
|
glGenTextures(1, s_symbols);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, s_symbols[0]);
|
||||||
|
glfwLoadTexture2D("symbols512.tga", GLFW_BUILD_MIPMAPS_BIT);
|
||||||
|
// Use trilinear interpolation for minification
|
||||||
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
// Use bilinear interpolation for magnification
|
||||||
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
// Enable texturing
|
||||||
|
//glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
int s1 = FX_OFF;
|
||||||
|
int s2 = FX_OFF;
|
||||||
|
int s3 = FX_OFF;
|
||||||
|
int s6 = FX_OFF;
|
||||||
|
|
||||||
|
float fade = 0.0f;
|
||||||
|
float flash = 0.0f;
|
||||||
|
float binger = 0.0f;
|
||||||
|
|
||||||
|
audio_volume(volume * 0.01);
|
||||||
|
|
||||||
|
int bing = 0;
|
||||||
|
|
||||||
|
int mode = 0;
|
||||||
|
uint32_t mode_timer = 0;
|
||||||
|
float hold = 0;
|
||||||
|
int hold2 = 0;
|
||||||
|
int ack = 0;
|
||||||
|
|
||||||
|
get_car("UF1", &car);
|
||||||
|
rpm.rangemax = (int)(car.maxrpm / 1000 + 2) * 1000.0;
|
||||||
|
|
||||||
|
float maxspeed = 0;
|
||||||
|
float speedtimer = 0;
|
||||||
|
float zoom = 65536.0;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
|
||||||
|
double curtime = glfwGetTime();
|
||||||
|
if (curtime >= next)
|
||||||
|
{
|
||||||
|
next = curtime + 0.01;
|
||||||
|
float interval = curtime - last_time;
|
||||||
|
|
||||||
|
flash += interval;
|
||||||
|
if (flash >= 1) flash = 0;
|
||||||
|
|
||||||
|
if (bing == 2)
|
||||||
|
{
|
||||||
|
binger += interval;
|
||||||
|
if (binger >= 1) {
|
||||||
|
binger = 0;
|
||||||
|
if (!g_fade) audio_play(3, FX_BING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_time = curtime;
|
||||||
|
|
||||||
|
if (speedtimer > 0) {
|
||||||
|
speedtimer -= interval;
|
||||||
|
if (speedtimer <= 0)
|
||||||
|
{
|
||||||
|
speedtimer = 0;
|
||||||
|
maxspeed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_pressed == 3) {
|
||||||
|
if (g_outgauge.time - g_shift_time > 1000 &&
|
||||||
|
g_outgauge.time - g_ctrl_time > 1000)
|
||||||
|
{
|
||||||
|
//if (g_released == 3)
|
||||||
|
//{
|
||||||
|
g_pressed = 0;
|
||||||
|
g_released = 0;
|
||||||
|
audio_play(4, FX_BIP);
|
||||||
|
mode++;
|
||||||
|
mode_timer = g_outgauge.time;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ack == 0) {
|
||||||
|
if (mode == 1) {
|
||||||
|
if (g_pressed == 1 && volume > 0) {
|
||||||
|
volume -= 10;
|
||||||
|
if (volume < 0) volume = 0;
|
||||||
|
audio_volume(volume * 0.01);
|
||||||
|
audio_play(4, FX_BOP);
|
||||||
|
ack = 1;
|
||||||
|
}
|
||||||
|
if (g_pressed == 2 && volume < 100) {
|
||||||
|
volume += 10;
|
||||||
|
if (volume > 100) volume = 100;
|
||||||
|
audio_volume(volume * 0.01);
|
||||||
|
audio_play(4, FX_BIP);
|
||||||
|
ack = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acknowledge key release
|
||||||
|
if (g_released)
|
||||||
|
{
|
||||||
|
g_pressed &= ~g_released;
|
||||||
|
g_released = 0;
|
||||||
|
mode_timer = g_outgauge.time;
|
||||||
|
ack = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode && g_outgauge.time - mode_timer > 2000)
|
||||||
|
{
|
||||||
|
mode = 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
}
|
||||||
|
if (g_released == 1) {
|
||||||
|
volume -= 10;
|
||||||
|
if (volume < 0) volume = 0;
|
||||||
|
audio_volume(volume * 0.01);
|
||||||
|
audio_play(4, FX_BOP);
|
||||||
|
}
|
||||||
|
if (g_released == 2) {
|
||||||
|
volume += 10;
|
||||||
|
if (volume > 100) volume = 100;
|
||||||
|
audio_volume(volume * 0.01);
|
||||||
|
audio_play(4, FX_BIP);
|
||||||
|
}
|
||||||
|
if (g_released == 3) {
|
||||||
|
g_pressed = 0;
|
||||||
|
g_released = 0;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwGetWindowSize(&width, &height);
|
||||||
|
height = height > 0 ? height : 1;
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glEnable(GL_ALPHA_TEST);
|
||||||
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
glOrtho(0, width, 0, height, -100, 100);
|
||||||
|
|
||||||
|
//socket_run();
|
||||||
|
|
||||||
|
/* Car change */
|
||||||
|
if (*g_outgauge.car != '\0' && strcmp(g_outgauge.car, car.tag))
|
||||||
|
{
|
||||||
|
int carnum = get_car(g_outgauge.car, &car);
|
||||||
|
if (carnum > -1)
|
||||||
|
{
|
||||||
|
rpm.rangemax = (int)(car.maxrpm / 1000 + 2) * 1000.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float speedunit = (g_outgauge.flags & OG_KM) ? 3.6 : 2.23693629;
|
||||||
|
float distunit = (g_outgauge.flags & OG_KM) ? 0.001 : 0.0006215;
|
||||||
|
|
||||||
|
if (g_outgauge.speed > maxspeed) {
|
||||||
|
maxspeed = g_outgauge.speed;
|
||||||
|
speedtimer = 15.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float litres = g_consumption * car.maxfuel * 60 * 60;
|
||||||
|
float hurr;
|
||||||
|
if (g_outgauge.flags & OG_KM)
|
||||||
|
{
|
||||||
|
hurr = litres / (g_outgauge.speed * speedunit) * 100;
|
||||||
|
speed.rangemax = (int)(car.maxspeed * 1.609f / 10) * 10 + 10;
|
||||||
|
speed2.rangemax = speed.rangemax / 1.609f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float gal = litres * 0.219969157;
|
||||||
|
hurr = g_outgauge.speed * speedunit / gal;
|
||||||
|
//printf("mpg %f\n", hurr);
|
||||||
|
speed.rangemax = car.maxspeed;
|
||||||
|
speed2.rangemax = speed.rangemax * 1.609f;
|
||||||
|
}
|
||||||
|
if (hurr < 0) hurr = 0;
|
||||||
|
if (hurr > 100) hurr = 100;
|
||||||
|
|
||||||
|
if (speed.rangemax < 150) {
|
||||||
|
speed.majorstep = 10;
|
||||||
|
speed.minorstep = 2;
|
||||||
|
} else {
|
||||||
|
speed.majorstep = 20;
|
||||||
|
speed.minorstep = 10;
|
||||||
|
}
|
||||||
|
if (speed2.rangemax < 150) {
|
||||||
|
speed2.majorstep = 10;
|
||||||
|
speed2.minorstep = 2;
|
||||||
|
} else {
|
||||||
|
speed2.majorstep = 20;
|
||||||
|
speed2.minorstep = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
float gsize = width * 0.20;
|
||||||
|
if (gsize > height * 0.4) gsize = height * 0.4;
|
||||||
|
speed.radius = gsize * 0.9;
|
||||||
|
speed2.radius = speed.radius * 0.6;
|
||||||
|
rpm.radius = speed.radius;
|
||||||
|
|
||||||
|
float msize = gsize * 0.2;
|
||||||
|
float symsize = gsize * 0.22;
|
||||||
|
|
||||||
|
float sgsize = height * 0.25;
|
||||||
|
fuel.radius = sgsize * 0.8;
|
||||||
|
boost.radius = sgsize * 0.8;
|
||||||
|
mpg.radius = sgsize * 0.8;
|
||||||
|
oil.radius = sgsize * 0.8;
|
||||||
|
|
||||||
|
float lsize = sgsize * 0.16;
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
if (rpmleft) {
|
||||||
|
glTranslatef(width * 0.5 + gsize * 1.5, height - gsize, 0);
|
||||||
|
} else {
|
||||||
|
glTranslatef(width * 0.5 - gsize * 1.5, height - gsize, 0);
|
||||||
|
}
|
||||||
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||||
|
snprintf(text, sizeof text, "%d", (int)(g_outgauge.speed * speedunit));
|
||||||
|
drawText(text, font, 20, -speed.radius * 0.8, msize, msize, TA_RIGHT, TA_BOTTOM);
|
||||||
|
drawText((g_outgauge.flags & OG_KM) ? "km/h" : "mph", font, 26, -speed.radius * 0.8, msize * 0.3, msize * 0.4, TA_LEFT, TA_BOTTOM);
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_PITSPEED)) {
|
||||||
|
drawSymbol2(DL_PITSPEED, g_outgauge.showlights & (1 << DL_PITSPEED), 1, 0, gsize * -0.4, symsize * 1.3, symsize * 1.3);
|
||||||
|
}
|
||||||
|
if (maxspeed > 0.001f)
|
||||||
|
{
|
||||||
|
glPushMatrix();
|
||||||
|
glScalef(speed.radius, speed.radius, 1.0);
|
||||||
|
drawDial(&speed, maxspeed * speedunit, GT_LINE);
|
||||||
|
glPopMatrix();
|
||||||
|
if (speedtimer < 13)
|
||||||
|
{
|
||||||
|
snprintf(text, sizeof text, "%0.1f%s", maxspeed * speedunit, (g_outgauge.flags & OG_KM) ? "km/h" : "mph");
|
||||||
|
drawText(text, font, 0, -speed.radius * 0.6, msize, msize, TA_CENTRE, TA_BOTTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw_gauge(&speed2, 0, -1, font, GT_NONE);
|
||||||
|
draw_gauge(&speed, g_outgauge.speed * speedunit, -1, font, GT_NEEDLE);
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
if (rpmleft) {
|
||||||
|
glTranslatef(width * 0.5 - gsize * 1.5, height - gsize, 0);
|
||||||
|
} else {
|
||||||
|
glTranslatef(width * 0.5 + gsize * 1.5, height - gsize, 0);
|
||||||
|
}
|
||||||
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||||
|
snprintf(text, sizeof text, "%d", (int)(g_outgauge.rpm));
|
||||||
|
drawText(text, font, 40, -rpm.radius * 0.8, msize, msize, TA_RIGHT, TA_BOTTOM);
|
||||||
|
drawText("rpm", font, 46, -rpm.radius * 0.8, msize * 0.3, msize * 0.4, TA_LEFT, TA_BOTTOM);
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_SHIFT)) {
|
||||||
|
drawSymbol2(DL_SHIFT, g_outgauge.showlights & (1 << DL_SHIFT), 1, 0, gsize * -0.4, symsize * 1.3, symsize * 1.3);
|
||||||
|
}
|
||||||
|
draw_gauge(&rpm, g_outgauge.rpm, car.maxrpm, font, GT_NEEDLE);
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
if (g_owncar == g_outgauge.playerid) {
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width * 0.5 - gsize * 1.5, fuel.radius * 0.75, 0);
|
||||||
|
drawSymbol2(4, 0, 0, fuel.radius * -0.65, 0, lsize, lsize);
|
||||||
|
if (g_outgauge.fuel <= warn_fuel2) {
|
||||||
|
if (flash < 0.5) {
|
||||||
|
drawSymbol2(19, 1, 0, -fuel.radius * 0.6, -fuel.radius * 0.3, lsize, lsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (g_outgauge.fuel <= warn_fuel1) {
|
||||||
|
drawSymbol2(19, 0, 0, -fuel.radius * 0.6, -fuel.radius * 0.3, lsize * 0.6, lsize * 0.6);
|
||||||
|
}
|
||||||
|
draw_gauge(&fuel, g_outgauge.fuel, -1, font, GT_BAR);
|
||||||
|
|
||||||
|
drawSymbol2(5, 0, 0, mpg.radius * 0.65, 0, lsize, lsize);
|
||||||
|
draw_gauge(&mpg, hurr, -1, font, GT_BAR);
|
||||||
|
|
||||||
|
float tcsize = sgsize * 0.08;
|
||||||
|
|
||||||
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||||
|
drawText("Trip Computer", font, 0, tcsize * 4.8, tcsize, tcsize, TA_CENTRE, TA_BOTTOM);
|
||||||
|
//snprintf(text, sizeof text, "dist %0.1f time %0.1f fuel %0.1f", s_cars[carnum].dist * distunit, s_cars[carnum].time, s_cars[carnum].cons);
|
||||||
|
snprintf(text, sizeof text, "Rem: %0.1f%%", g_outgauge.fuel * 100);
|
||||||
|
drawText(text, font, -fuel.radius * 0.3, tcsize * 3.6, tcsize, tcsize, TA_LEFT, TA_BOTTOM);
|
||||||
|
snprintf(text, sizeof text, "Eco: %0.1f %s", hurr, (g_outgauge.flags & OG_KM) ? "l/100km" : "mpg");
|
||||||
|
drawText(text, font, -fuel.radius * 0.3, tcsize * 2.4, tcsize, tcsize, TA_LEFT, TA_BOTTOM);
|
||||||
|
|
||||||
|
float dist = litres / (g_outgauge.speed * speedunit) * (g_outgauge.fuel * car.maxfuel) * distunit;
|
||||||
|
snprintf(text, sizeof text, "Dist: %0.1f %s", dist, (g_outgauge.flags & OG_KM) ? "km" : "miles");
|
||||||
|
drawText(text, font, -fuel.radius * 0.3, tcsize * 1.2, tcsize, tcsize, TA_LEFT, TA_BOTTOM);
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width * 0.5 + gsize * 1.5, oil.radius * 0.75, 0);
|
||||||
|
|
||||||
|
if (g_outgauge.flags & OG_TURBO)
|
||||||
|
{
|
||||||
|
if (g_outgauge.flags & OG_BAR) {
|
||||||
|
boost.majorstep = 0.4;
|
||||||
|
boost.minorstep = 0.1;
|
||||||
|
} else {
|
||||||
|
boost.majorstep = 2.0;
|
||||||
|
boost.minorstep = 1.0;
|
||||||
|
}
|
||||||
|
float mult = (g_outgauge.flags & OG_BAR) ? 1.0 : 14.5037738;
|
||||||
|
boost.rangemax = (int)(car.maxboost * mult / boost.majorstep + 1) * boost.majorstep;
|
||||||
|
|
||||||
|
drawSymbol2(6, 0, 0, boost.radius * -0.65, 0, lsize, lsize);
|
||||||
|
draw_gauge(&boost, g_outgauge.turbo * mult, -1, font, GT_BAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
//drawSymbol2(7, 0, 0, oil.radius * 0.65, 0, lsize, lsize);
|
||||||
|
//draw_gauge(&oil, g_outgauge.oiltemp, -1, font, GT_BAR);
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
//glPushMatrix();
|
||||||
|
//glTranslatef(width * 0.5 + gsize * 1.5, boost.radius * 0.75, 0);
|
||||||
|
|
||||||
|
time_t rawtime;
|
||||||
|
time(&rawtime);
|
||||||
|
struct tm *timeinfo = localtime(&rawtime);
|
||||||
|
//draw_gauge(&clock, (timeinfo->tm_hour % 12) + timeinfo->tm_min / 60.0, -1, font, GT_NEEDLE);
|
||||||
|
//glPopMatrix();
|
||||||
|
|
||||||
|
//printf("%f %f\n", g_outgauge.oilpressure, g_outgauge.oiltemp);
|
||||||
|
|
||||||
|
float symspace = symsize * -1.2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width * 0.5 + symspace, height - gsize * 0.25 + symspace * 6, 0.0f);
|
||||||
|
input.radius = symsize * 0.5;
|
||||||
|
input.dial.r = 0; input.dial.g = 0; input.dial.b = 1;
|
||||||
|
draw_gauge(&input, g_outgauge.clutch, -1, font, GT_BAR);
|
||||||
|
glTranslatef(-symspace, 0, 0);
|
||||||
|
input.dial.r = 1; input.dial.g = 0; input.dial.b = 0;
|
||||||
|
draw_gauge(&input, g_outgauge.brake, -1, font, GT_BAR);
|
||||||
|
glTranslatef(-symspace, 0, 0);
|
||||||
|
input.dial.r = 0; input.dial.g = 1; input.dial.b = 0;
|
||||||
|
draw_gauge(&input, g_outgauge.throttle, -1, font, GT_BAR);
|
||||||
|
glPopMatrix();
|
||||||
|
*/
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width * 0.5, height - gsize * 0.25, 0.0f);
|
||||||
|
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_SIGNAL_ANY))
|
||||||
|
{
|
||||||
|
drawSymbol2(DL_SIGNAL_ANY, g_outgauge.showlights & (1 << DL_SIGNAL_ANY), 1, 0, 0, symsize, symsize);
|
||||||
|
} else {
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_SIGNAL_L)) {
|
||||||
|
drawSymbol2(DL_SIGNAL_L, g_outgauge.showlights & (1 << DL_SIGNAL_L), 1, -symsize * 0.75, 0, symsize, symsize);
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_SIGNAL_R)) {
|
||||||
|
drawSymbol2(DL_SIGNAL_R, g_outgauge.showlights & (1 << DL_SIGNAL_R), 1, symsize * 0.75, 0, symsize, symsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_FULLBEAM)) {
|
||||||
|
drawSymbol2(DL_FULLBEAM, g_outgauge.showlights & (1 << DL_FULLBEAM), 1, 0, symspace, symsize, symsize);
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_HANDBRAKE)) {
|
||||||
|
drawSymbol2(DL_HANDBRAKE, g_outgauge.showlights & (1 << DL_HANDBRAKE), 1, 0, symspace * 2, symsize, symsize);
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_ABS)) {
|
||||||
|
drawSymbol2(DL_ABS, g_outgauge.showlights & (1 << DL_ABS), 1, 0, symspace * 3, symsize, symsize);
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_TC)) {
|
||||||
|
drawSymbol2(DL_TC, g_outgauge.showlights & (1 << DL_TC), 1, 0, symspace * 4, symsize, symsize);
|
||||||
|
}
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_OILWARN)) {
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_BATTERY)) {
|
||||||
|
drawSymbol2(DL_BATTERY, g_outgauge.showlights & (1 << DL_BATTERY), 1, -symsize * 0.75, symspace * 5, symsize, symsize);
|
||||||
|
}
|
||||||
|
drawSymbol2(DL_OILWARN, g_outgauge.showlights & (1 << DL_OILWARN), 1, symsize * 0.75, symspace * 5, symsize, symsize);
|
||||||
|
} else {
|
||||||
|
if (g_outgauge.dashlights & (1 << DL_BATTERY)) {
|
||||||
|
drawSymbol2(DL_BATTERY, g_outgauge.showlights & (1 << DL_BATTERY), 1, 0, symspace * 5, symsize, symsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width, 0, 0);
|
||||||
|
glScalef(-height * 0.075, height * 0.075, 1.0);
|
||||||
|
glColor4f(0.15, 1.0, 0.15, 0.8);
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
|
glVertex3f(0.0, 0.0, 0.0);
|
||||||
|
glVertex3f(0.25, 0.0, 0.0);
|
||||||
|
glVertex3f(0.0, g_outgauge.throttle, 0.0);
|
||||||
|
glVertex3f(0.25, g_outgauge.throttle, 0.0);
|
||||||
|
glEnd();
|
||||||
|
glColor4f(1.0, 0.15, 0.15, 0.8);
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
|
glVertex3f(0.30, 0.0, 0.0);
|
||||||
|
glVertex3f(0.55, 0.0, 0.0);
|
||||||
|
glVertex3f(0.30, g_outgauge.brake, 0.0);
|
||||||
|
glVertex3f(0.55, g_outgauge.brake, 0.0);
|
||||||
|
glEnd();
|
||||||
|
glColor4f(0.15, 0.15, 1.0, 0.8);
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
|
glVertex3f(0.60, 0.0, 0.0);
|
||||||
|
glVertex3f(0.85, 0.0, 0.0);
|
||||||
|
glVertex3f(0.60, g_outgauge.clutch, 0.0);
|
||||||
|
glVertex3f(0.85, g_outgauge.clutch, 0.0);
|
||||||
|
glEnd();
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glColor4f(0.15, 0.15, 1.0, 1.0);
|
||||||
|
glTranslatef(width * 0.5, 0, 0.0f);
|
||||||
|
if (g_outgauge.gear == 0) {
|
||||||
|
snprintf(text, sizeof text, "R");
|
||||||
|
} else if (g_outgauge.gear == 1) {
|
||||||
|
snprintf(text, sizeof text, "N");
|
||||||
|
} else {
|
||||||
|
snprintf(text, sizeof text, "%d", g_outgauge.gear - 1);
|
||||||
|
}
|
||||||
|
drawText(text, font, 0, sgsize * 0.7, sgsize * 0.7, sgsize * 0.7, TA_CENTRE, TA_BOTTOM);
|
||||||
|
snprintf(text, sizeof text, "%s", s_carinfo[g_outgauge.playerid].cname);
|
||||||
|
drawText(text, font, 0, sgsize * 0.4, sgsize * 0.2, sgsize * 0.2, TA_CENTRE, TA_BOTTOM);
|
||||||
|
snprintf(text, sizeof text, "%s", s_conninfo[s_carinfo[g_outgauge.playerid].ucid].uname);
|
||||||
|
drawText(text, font, 0, sgsize * 0.2, sgsize * 0.15, sgsize * 0.15, TA_CENTRE, TA_BOTTOM);
|
||||||
|
//snprintf(text, sizeof text, "dist %0.1f time %0.1f fuel %0.1f", s_cars[carnum].dist * distunit, s_cars[carnum].time, s_cars[carnum].cons);
|
||||||
|
//snprintf(text, sizeof text, "p %d r %d; %d %d, %u", g_pressed, g_released, volume, mode, mode_timer);
|
||||||
|
//drawText(text, font, 0, sgsize * 1.2, sgsize * 0.2, sgsize * 0.2, TA_CENTRE, TA_CENTRE);
|
||||||
|
//snprintf(text, sizeof text, "s %u c %u; %u", g_shift_time, g_ctrl_time, g_outgauge.time);
|
||||||
|
//drawText(text, font, 0, sgsize * 1.5, sgsize * 0.2, sgsize * 0.2, TA_CENTRE, TA_CENTRE);
|
||||||
|
snprintf(text, sizeof text, "%02d:%02d", timeinfo->tm_hour, timeinfo->tm_min);
|
||||||
|
drawText(text, font, 0, sgsize * 1.3, sgsize * 0.2, sgsize * 0.2, TA_CENTRE, TA_BOTTOM);
|
||||||
|
glPopMatrix();
|
||||||
|
/*
|
||||||
|
int ox = s_carinfo[g_outgauge.playerid].x;
|
||||||
|
int oy = s_carinfo[g_outgauge.playerid].y;
|
||||||
|
int oz = s_carinfo[g_outgauge.playerid].z;
|
||||||
|
int head = s_carinfo[g_outgauge.playerid].heading;
|
||||||
|
int speed = s_carinfo[g_outgauge.playerid].speed;
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(width / 2, height / 2, 0);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
struct carinfo *ci = s_carinfo + i;
|
||||||
|
if (!ci->active) continue;
|
||||||
|
|
||||||
|
float dx = (ci->x - ox) / zoom;
|
||||||
|
float dy = (ci->y - oy) / zoom;
|
||||||
|
|
||||||
|
//float ddx = (ci->x - ox) / 65536.0;
|
||||||
|
//float ddy = (ci->y - oy) / 65536.0;
|
||||||
|
//ci->dist = sqrtf(ddx * ddx + ddy * ddy);
|
||||||
|
|
||||||
|
// if (ci->dist >= width / 2 && ci->dist >= height / 2) continue;
|
||||||
|
|
||||||
|
const struct conninfo *pi = s_conninfo + ci->ucid;
|
||||||
|
int idx = (ci->x - ox) >> 16;
|
||||||
|
int idy = (ci->y - oy) >> 16;
|
||||||
|
int dist = sqrt(idx * idx + idy * idy);
|
||||||
|
|
||||||
|
snprintf(text, sizeof text, "%s - %d (%0.1f %s)", pi->uname, dist, (ci->speed - speed) / 327.68f * speedunit, (g_outgauge.flags & OG_KM) ? "km/h" : "mph");
|
||||||
|
//printf("%d %s %d %d\n", i, text, head / 182, ci->heading / 182);
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glRotatef(-head / 182.0444f, 0.0, 0.0, 1.0);
|
||||||
|
glTranslatef(dx, dy, 0);
|
||||||
|
if (ci->hist > 0) {
|
||||||
|
glBegin(GL_LINE_STRIP);
|
||||||
|
int j;
|
||||||
|
for (j = CARPATHSIZE; j > 0; j--)
|
||||||
|
{
|
||||||
|
int d = (j + ci->hist - 1) % CARPATHSIZE;
|
||||||
|
if (ci->hist_x[d] == 0 && ci->hist_y[d] == 0) continue;
|
||||||
|
glColor4f(ci->hist_s[d] / 32768.0f, 1.0 - ci->hist_s[d] / 32768.0f, 0.0, 0.9);
|
||||||
|
glVertex3f((ci->hist_x[d] - ci->x) / zoom, (ci->hist_y[d] - ci->y) / zoom, 0.0);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
if (pi->state == 1) {
|
||||||
|
glColor4f(1.0, 0.0, 0.0, 0.5);
|
||||||
|
} else if (pi->state == 2) {
|
||||||
|
glColor4f(0.0, 1.0, 1.0, 0.5);
|
||||||
|
} else {
|
||||||
|
glColor4f(2.0, 1.0, 1.0, 0.5);
|
||||||
|
}
|
||||||
|
glRotatef(head / 182.0444f, 0.0, 0.0, 1.0);
|
||||||
|
drawText(text, font, 5, 3, 10, 10, TA_LEFT, TA_TOP);
|
||||||
|
glRotatef(-(head - ci->heading) / 182.0444, 0.0, 0.0, 1.0);
|
||||||
|
glBegin(GL_TRIANGLES);
|
||||||
|
glVertex3f(0, 5, 0);
|
||||||
|
glVertex3f(2.5, -5, 0);
|
||||||
|
glVertex3f(-2.5, -5, 0);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
glPopMatrix();
|
||||||
|
*/
|
||||||
|
|
||||||
|
int os1 = (g_outgauge.showlights & (1 << DL_SIGNAL_L)) ? FX_ON : FX_OFF;
|
||||||
|
int os2 = (g_outgauge.showlights & (1 << DL_SIGNAL_R)) ? FX_ON : FX_OFF;
|
||||||
|
int os3 = (g_outgauge.showlights & (1 << DL_SIGNAL_ANY)) ? FX_ON : FX_OFF;
|
||||||
|
int os6 = (g_outgauge.showlights & (1 << DL_PITSPEED)) ? FX_ON : FX_OFF;
|
||||||
|
|
||||||
|
if (s1 != os1) {
|
||||||
|
s1 = os1;
|
||||||
|
if (!g_fade) audio_play(0, s1);
|
||||||
|
}
|
||||||
|
if (s2 != os2) {
|
||||||
|
s2 = os2;
|
||||||
|
if (!g_fade) audio_play(1, s2);
|
||||||
|
}
|
||||||
|
if (s3 != os3) {
|
||||||
|
s3 = os3;
|
||||||
|
if (!g_fade) audio_play(2, s3);
|
||||||
|
}
|
||||||
|
if (s6 != os6) {
|
||||||
|
s6 = os6;
|
||||||
|
if (!g_fade) audio_play(5, s6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_outgauge.fuel > warn_fuel1 || g_outgauge.rpm == 0)
|
||||||
|
{
|
||||||
|
bing = 0;
|
||||||
|
}
|
||||||
|
else if (g_outgauge.fuel <= warn_fuel2)
|
||||||
|
{
|
||||||
|
bing = 2;
|
||||||
|
}
|
||||||
|
else if (bing != 1 && g_outgauge.fuel <= warn_fuel1)
|
||||||
|
{
|
||||||
|
bing = 1;
|
||||||
|
if (!g_fade) audio_play(3, FX_BING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_fade && fade < 0.8) {
|
||||||
|
fade = fade + 0.01;
|
||||||
|
} else if (!g_fade && fade > 0.0) {
|
||||||
|
fade = fade - 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//glPopMatrix();
|
||||||
|
|
||||||
|
if (dofade && fade > 0) {
|
||||||
|
glColor4f(0.0, 0.0, 0.0, fade);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glVertex3f(0.0, 0.0, 0.0);
|
||||||
|
glVertex3f(width, 0.0, 0.0);
|
||||||
|
glVertex3f(width, height, 0.0);
|
||||||
|
glVertex3f(0.0, height, 0.0);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSwapBuffers();
|
||||||
|
|
||||||
|
// printf("%f\n", g_outgauge.speed * 2.23693629);
|
||||||
|
|
||||||
|
running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam( GLFW_OPENED) && !glfwGetMouseButton(1);
|
||||||
|
/*
|
||||||
|
int wheel = glfwGetMouseWheel();
|
||||||
|
if (wheel >= 1) {
|
||||||
|
zoom = 65536.0 * wheel;
|
||||||
|
} else {
|
||||||
|
zoom = 65536 / (-wheel + 2);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//printf("%u %u %u\n", g_outgauge.flags, g_outgauge.dashlights, g_outgauge.showlights);
|
||||||
|
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_running = 0;
|
||||||
|
|
||||||
|
audio_deinit();
|
||||||
|
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
network_worker_deinit();
|
||||||
|
|
||||||
|
config_set_int("port", g_outgauge_port);
|
||||||
|
config_set_int("fullscreen", fullscreen);
|
||||||
|
config_set_int("width", width);
|
||||||
|
config_set_int("height", height);
|
||||||
|
config_set_int("volume", volume);
|
||||||
|
config_set_int("rpmleft", rpmleft);
|
||||||
|
config_set_int("fade", dofade);
|
||||||
|
config_set_string("insim_host", insim_host);
|
||||||
|
config_set_int("insim_port", g_insim_port);
|
||||||
|
config_set_float("warn_fuel1", warn_fuel1);
|
||||||
|
config_set_float("warn_fuel2", warn_fuel2);
|
||||||
|
config_deinit();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define LIST(X, T, compare_func) \
|
||||||
|
struct X ## _list_t \
|
||||||
|
{ \
|
||||||
|
size_t used; \
|
||||||
|
size_t size; \
|
||||||
|
T *items; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static inline void X ## _list_init(struct X ## _list_t *list) \
|
||||||
|
{ \
|
||||||
|
list->used = 0; \
|
||||||
|
list->size = 0; \
|
||||||
|
list->items = NULL; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void X ## _list_free(struct X ## _list_t *list) \
|
||||||
|
{ \
|
||||||
|
free(list->items); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void X ## _list_add(struct X ## _list_t *list, T item) \
|
||||||
|
{ \
|
||||||
|
if (list->used >= list->size) \
|
||||||
|
{ \
|
||||||
|
list->size += 64U; \
|
||||||
|
T *new_items = realloc(list->items, sizeof *list->items * list->size); \
|
||||||
|
if (new_items == NULL) \
|
||||||
|
{ \
|
||||||
|
fprintf(stderr, "Reallocating %s list to %u items (%u bytes) failed\n", #X, (unsigned)list->size, (unsigned)(sizeof *list->items * list->size)); \
|
||||||
|
list->size -= 64U; \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
list->items = new_items; \
|
||||||
|
} \
|
||||||
|
list->items[list->used++] = item; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void X ## _list_del_item(struct X ## _list_t *list, T item) \
|
||||||
|
{ \
|
||||||
|
size_t i; \
|
||||||
|
for (i = 0; i < list->used; i++) \
|
||||||
|
{ \
|
||||||
|
if (compare_func(&list->items[i], &item)) { \
|
||||||
|
list->items[i] = list->items[--list->used]; \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void X ## _list_del_index(struct X ## _list_t *list, size_t index) \
|
||||||
|
{ \
|
||||||
|
list->items[index] = list->items[--list->used]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline int X ## _list_contains(struct X ## _list_t *list, T item) \
|
||||||
|
{ \
|
||||||
|
size_t i; \
|
||||||
|
for (i = 0; i < list->used; i++) \
|
||||||
|
{ \
|
||||||
|
if (compare_func(&list->items[i], &item)) { \
|
||||||
|
return 1; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LIST_H */
|
|
@ -0,0 +1,194 @@
|
||||||
|
#define _WIN32_WINNT 0x0501
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
//#include "network.h"
|
||||||
|
#include "network_worker.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
|
static struct worker s_network_worker;
|
||||||
|
|
||||||
|
struct network_job
|
||||||
|
{
|
||||||
|
char *host;
|
||||||
|
int port;
|
||||||
|
network_callback callback;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
int resolve(const char *hostname, int port, struct sockaddr_in *addr)
|
||||||
|
{
|
||||||
|
struct addrinfo *ai;
|
||||||
|
struct addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof hints);
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
#ifndef WIN32
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
#endif
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
char port_name[6];
|
||||||
|
snprintf(port_name, sizeof port_name, "%u", port);
|
||||||
|
|
||||||
|
int e = getaddrinfo(hostname, port_name, &hints, &ai);
|
||||||
|
|
||||||
|
if (e != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "getaddrinfo: %s\n", strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo *runp;
|
||||||
|
for (runp = ai; runp != NULL; runp = runp->ai_next)
|
||||||
|
{
|
||||||
|
struct sockaddr_in *ai_addr = (struct sockaddr_in *)runp->ai_addr;
|
||||||
|
|
||||||
|
/* Take the first address */
|
||||||
|
*addr = *ai_addr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void network_worker(void *arg)
|
||||||
|
{
|
||||||
|
struct network_job *job = arg;
|
||||||
|
|
||||||
|
if (job->callback != NULL)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
if (!resolve(job->host, job->port, &addr))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unable to resolve %s:%d\n", job->host, job->port);
|
||||||
|
if (job->callback != NULL)
|
||||||
|
{
|
||||||
|
job->callback(-1, job->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (job->callback != NULL)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
fprintf(stderr, "socket: %d\n", WSAGetLastError());
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "socket: %s\n", strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
socket_set_nonblock(fd);
|
||||||
|
socket_set_nodelay(fd);
|
||||||
|
if (connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "connect: %d\n", WSAGetLastError());
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (errno != EINPROGRESS && errno != EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "connect: %s\n", strerror(errno));
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
job->callback(fd, job->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(job->host);
|
||||||
|
free(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void network_worker_init(void)
|
||||||
|
{
|
||||||
|
worker_init(&s_network_worker, "network", 60000, 1, network_worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void network_worker_deinit(void)
|
||||||
|
{
|
||||||
|
worker_deinit(&s_network_worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *network_connect(const char *host, int port, network_callback callback, void *data)
|
||||||
|
{
|
||||||
|
struct network_job *job = malloc(sizeof *job);
|
||||||
|
job->host = strdup(host);
|
||||||
|
job->port = port;
|
||||||
|
job->callback = callback;
|
||||||
|
job->data = data;
|
||||||
|
|
||||||
|
worker_queue(&s_network_worker, job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
int network_listen(int port, int tcp)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
#ifdef USE_IPV6
|
||||||
|
fd = socket(AF_INET6, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
|
||||||
|
#else
|
||||||
|
fd = socket(AF_INET, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
fprintf(stderr, "socket: %d\n", WSAGetLastError());
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "socket: %s\n", strerror(errno));
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_IPV6
|
||||||
|
struct sockaddr_in6 serv_addr;
|
||||||
|
serv_addr.sin6_family = AF_INET6;
|
||||||
|
serv_addr.sin6_addr = in6addr_any;
|
||||||
|
serv_addr.sin6_port = htons(port);
|
||||||
|
#else
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int on = 1;
|
||||||
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof on) == -1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "setsockopt: Could not set SO_REUSEADDR: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(fd, (struct sockaddr *)&serv_addr, sizeof serv_addr) < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "bind: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_set_nonblock(fd);
|
||||||
|
if (tcp) socket_set_nodelay(fd);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef NETWORK_WORKER_H
|
||||||
|
#define NETWORK_WORKER_H
|
||||||
|
|
||||||
|
typedef void (*network_callback)(int fd, void *data);
|
||||||
|
|
||||||
|
void network_worker_init(void);
|
||||||
|
void network_worker_deinit(void);
|
||||||
|
|
||||||
|
void *network_connect(const char *host, int port, network_callback callback, void *data);
|
||||||
|
int network_listen(int port, int tcp);
|
||||||
|
|
||||||
|
#endif /* NETWORK_WORKER_H */
|
|
@ -0,0 +1,105 @@
|
||||||
|
//#define USE_IPV6
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
//#include <ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "socket.h"
|
||||||
|
#include "network_worker.h"
|
||||||
|
#include "outgauge.h"
|
||||||
|
#include "cars.h"
|
||||||
|
|
||||||
|
struct outgauge g_outgauge;
|
||||||
|
int g_fade;
|
||||||
|
int g_pressed;
|
||||||
|
int g_released;
|
||||||
|
uint32_t g_shift_time;
|
||||||
|
uint32_t g_ctrl_time;
|
||||||
|
int g_owncar;
|
||||||
|
static int s_car;
|
||||||
|
|
||||||
|
static uint32_t s_prev_time;
|
||||||
|
static float s_prev_fuel;
|
||||||
|
float g_consumption;
|
||||||
|
|
||||||
|
int outgauge_recv(int fd, int can_write, int can_read, void *arg)
|
||||||
|
{
|
||||||
|
if (can_read)
|
||||||
|
{
|
||||||
|
struct outgauge o;
|
||||||
|
ssize_t len = recv(fd, (char *)&o, sizeof o, 0);
|
||||||
|
|
||||||
|
/* Ignore out of order packet */
|
||||||
|
//if (o.time < s_prev_time)
|
||||||
|
|
||||||
|
if (o.rpm < 0) o.rpm = 0;
|
||||||
|
memcpy(&g_outgauge, &o, len);
|
||||||
|
g_fade &= ~1;
|
||||||
|
|
||||||
|
g_released |= g_pressed & (~(o.flags & 3));
|
||||||
|
|
||||||
|
if (!(g_pressed & 1) && (o.flags & 1)) g_shift_time = o.time;
|
||||||
|
if (!(g_pressed & 2) && (o.flags & 2)) g_ctrl_time = o.time;
|
||||||
|
g_pressed |= (o.flags & 3);
|
||||||
|
|
||||||
|
if (g_owncar != o.playerid)
|
||||||
|
{
|
||||||
|
if (o.fuel > 0)
|
||||||
|
{
|
||||||
|
g_owncar = o.playerid;
|
||||||
|
g_consumption = 0;
|
||||||
|
s_car = get_car(o.car, NULL);
|
||||||
|
s_prev_fuel = o.fuel;
|
||||||
|
g_fade &= ~2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_fade |= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_fade &= ~2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_prev_time > 0)
|
||||||
|
{
|
||||||
|
if (o.fuel > s_prev_fuel)
|
||||||
|
{
|
||||||
|
g_consumption = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float interval = (o.time - s_prev_time) * 0.001;
|
||||||
|
if (interval > 0)
|
||||||
|
{
|
||||||
|
float weight = 0.004;
|
||||||
|
g_consumption = (1.0 - weight) * g_consumption + weight * (s_prev_fuel - o.fuel) / interval;
|
||||||
|
|
||||||
|
if (g_owncar == o.playerid)
|
||||||
|
{
|
||||||
|
update_car(s_car, o.speed * interval, interval, g_consumption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_prev_time = o.time;
|
||||||
|
s_prev_fuel = o.fuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void outgauge_init(int port)
|
||||||
|
{
|
||||||
|
int fd = network_listen(port, 0);
|
||||||
|
if (fd < 0) return;
|
||||||
|
register_socket(fd, SM_READ, &outgauge_recv, NULL);
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef OUTGAUGE_H
|
||||||
|
#define OUTGAUGE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct outgauge
|
||||||
|
{
|
||||||
|
uint32_t time;
|
||||||
|
char car[4];
|
||||||
|
uint16_t flags;
|
||||||
|
uint8_t gear;
|
||||||
|
uint8_t playerid;
|
||||||
|
float speed;
|
||||||
|
float rpm;
|
||||||
|
float turbo;
|
||||||
|
float enginetemp;
|
||||||
|
float fuel;
|
||||||
|
float oilpressure;
|
||||||
|
float oiltemp;
|
||||||
|
uint32_t dashlights;
|
||||||
|
uint32_t showlights;
|
||||||
|
float throttle;
|
||||||
|
float brake;
|
||||||
|
float clutch;
|
||||||
|
char display1[16];
|
||||||
|
char display2[16];
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct outgauge g_outgauge;
|
||||||
|
extern float g_consumption;
|
||||||
|
extern int g_owncar;
|
||||||
|
|
||||||
|
extern int g_fade;
|
||||||
|
extern int g_pressed;
|
||||||
|
extern int g_released;
|
||||||
|
extern uint32_t g_shift_time;
|
||||||
|
extern uint32_t g_ctrl_time;
|
||||||
|
|
||||||
|
|
||||||
|
void outgauge_init(int port);
|
||||||
|
|
||||||
|
#endif /* OUTGAUGE_H */
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
struct queue_object_t
|
||||||
|
{
|
||||||
|
struct queue_object_t *next;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct queue_t
|
||||||
|
{
|
||||||
|
pthread_mutex_t produce_mutex;
|
||||||
|
pthread_mutex_t consume_mutex;
|
||||||
|
struct queue_object_t *head;
|
||||||
|
struct queue_object_t *tail;
|
||||||
|
struct queue_object_t *divider;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct queue_t *queue_new(void)
|
||||||
|
{
|
||||||
|
struct queue_t *queue = malloc(sizeof *queue);
|
||||||
|
struct queue_object_t *qo = malloc(sizeof *qo);
|
||||||
|
qo->next = NULL;
|
||||||
|
qo->data = NULL;
|
||||||
|
|
||||||
|
queue->head = qo;
|
||||||
|
queue->tail = qo;
|
||||||
|
queue->divider = qo;
|
||||||
|
|
||||||
|
pthread_mutex_init(&queue->produce_mutex, NULL);
|
||||||
|
pthread_mutex_init(&queue->consume_mutex, NULL);
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_delete(struct queue_t *queue)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
while (queue->head != NULL)
|
||||||
|
{
|
||||||
|
struct queue_object_t *qo = queue->head;
|
||||||
|
queue->head = qo->next;
|
||||||
|
|
||||||
|
//free(qo->data);
|
||||||
|
free(qo);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&queue->produce_mutex);
|
||||||
|
pthread_mutex_destroy(&queue->consume_mutex);
|
||||||
|
|
||||||
|
free(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
int queue_produce(struct queue_t *queue, void *data)
|
||||||
|
{
|
||||||
|
if (queue == NULL) return 0;
|
||||||
|
|
||||||
|
struct queue_object_t *qo = malloc(sizeof *qo);
|
||||||
|
qo->next = NULL;
|
||||||
|
qo->data = data;
|
||||||
|
|
||||||
|
/* Lock needed? */
|
||||||
|
pthread_mutex_lock(&queue->produce_mutex);
|
||||||
|
queue->tail->next = qo;
|
||||||
|
queue->tail = qo;
|
||||||
|
/* */
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while (queue->head != queue->divider)
|
||||||
|
{
|
||||||
|
struct queue_object_t *qo = queue->head;
|
||||||
|
queue->head = qo->next;
|
||||||
|
|
||||||
|
//free(qo->data);
|
||||||
|
free(qo);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&queue->produce_mutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int queue_consume(struct queue_t *queue, void **data)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&queue->consume_mutex);
|
||||||
|
if (queue->divider != queue->tail)
|
||||||
|
{
|
||||||
|
*data = queue->divider->next->data;
|
||||||
|
queue->divider = queue->divider->next;
|
||||||
|
pthread_mutex_unlock(&queue->consume_mutex);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&queue->consume_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef QUEUE_H
|
||||||
|
#define QUEUE_H
|
||||||
|
|
||||||
|
struct queue_t;
|
||||||
|
|
||||||
|
struct queue_t *queue_new(void);
|
||||||
|
void queue_delete(struct queue_t *queue);
|
||||||
|
int queue_produce(struct queue_t *queue, void *data);
|
||||||
|
int queue_consume(struct queue_t *queue, void **data);
|
||||||
|
|
||||||
|
#endif /* QUEUE_H */
|
|
@ -0,0 +1,261 @@
|
||||||
|
#ifndef WIN32
|
||||||
|
//#define USE_POLL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#ifdef USE_POLL
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#endif /* USE_POLL */
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "socket.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
int s_running;
|
||||||
|
|
||||||
|
struct socket_t
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int mode;
|
||||||
|
socket_func socket_func;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int socket_compare(struct socket_t **a, struct socket_t **b)
|
||||||
|
{
|
||||||
|
return (*a)->fd == (*b)->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST(socket, struct socket_t *, socket_compare)
|
||||||
|
static struct socket_list_t s_sockets;
|
||||||
|
static pthread_mutex_t s_sockets_mutex;
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
static int s_epoll_fd = -1;
|
||||||
|
#endif /* USE_POLL */
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
static struct socket_t *socket_get_by_fd(int fd)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < s_sockets.used; i++)
|
||||||
|
{
|
||||||
|
struct socket_t *s = s_sockets.items[i];
|
||||||
|
if (s->fd == fd) return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void register_socket(int fd, int mode, socket_func socket_func, void *arg)
|
||||||
|
{
|
||||||
|
struct socket_t *s = malloc(sizeof *s);
|
||||||
|
s->fd = fd;
|
||||||
|
s->mode = mode;
|
||||||
|
s->socket_func = socket_func;
|
||||||
|
s->arg = arg;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_sockets_mutex);
|
||||||
|
socket_list_add(&s_sockets, s);
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
if (s_epoll_fd == -1)
|
||||||
|
{
|
||||||
|
s_epoll_fd = epoll_create(32);
|
||||||
|
if (s_epoll_fd == -1) fprintf(stderr, "register_socket(): epoll_create: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.ptr = s;
|
||||||
|
int r = epoll_ctl(s_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||||
|
if (r == -1) fprintf(stderr, "register_socket(): epoll_ctl: %s\n", strerror(errno));
|
||||||
|
#endif /* USE_POLL */
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&s_sockets_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_flag_write(int fd)
|
||||||
|
{
|
||||||
|
#ifdef USE_POLL
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLOUT;
|
||||||
|
ev.data.ptr = socket_get_by_fd(fd);;
|
||||||
|
int r = epoll_ctl(s_epoll_fd, EPOLL_CTL_MOD, fd, &ev);
|
||||||
|
if (r == -1) fprintf(stderr, "socket_flag_write(): epoll_ctl: %s\n", strerror(errno));
|
||||||
|
#endif /* USE_POLL */
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_clear_write(int fd)
|
||||||
|
{
|
||||||
|
#ifdef USE_POLL
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.ptr = socket_get_by_fd(fd);
|
||||||
|
int r = epoll_ctl(s_epoll_fd, EPOLL_CTL_MOD, fd, &ev);
|
||||||
|
if (r == -1) fprintf(stderr, "socket_clear_write(): epoll_ctl: %s\n", strerror(errno));
|
||||||
|
#endif /* USE_POLL */
|
||||||
|
}
|
||||||
|
|
||||||
|
void deregister_socket(int fd)
|
||||||
|
{
|
||||||
|
struct socket_t s;
|
||||||
|
s.fd = fd;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_sockets_mutex);
|
||||||
|
socket_list_del_item(&s_sockets, &s);
|
||||||
|
pthread_mutex_unlock(&s_sockets_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_init(void)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
WSADATA WSA_Data;
|
||||||
|
WSAStartup (0x101, & WSA_Data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_init(&s_sockets_mutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_deinit(void)
|
||||||
|
{
|
||||||
|
if (s_sockets.used > 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[network] socket_deinit(): %u sockets remaining in list\n", (unsigned)s_sockets.used);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
close(s_epoll_fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
socket_list_free(&s_sockets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_set_nonblock(int fd)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
u_long iMode = 1;
|
||||||
|
ioctlsocket(fd, FIONBIO, &iMode);
|
||||||
|
#else
|
||||||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
int res = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[network] socket_set_nonblocK(): Could not set nonblocking IO: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_set_nodelay(int fd)
|
||||||
|
{
|
||||||
|
int b = 1;
|
||||||
|
int res = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&b, sizeof b);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[network] socket_set_nonblock(): Could not set TCP_NODELAY: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_POLL
|
||||||
|
|
||||||
|
#define EPOLL_EVENTS 256
|
||||||
|
|
||||||
|
void *socket_run(void *arg)
|
||||||
|
{
|
||||||
|
s_running = 1;
|
||||||
|
|
||||||
|
while (s_running)
|
||||||
|
{
|
||||||
|
struct epoll_event events[EPOLL_EVENTS];
|
||||||
|
int n = epoll_wait(s_epoll_fd, events, EPOLL_EVENTS, 10000);
|
||||||
|
if (n == -1) fprintf(stderr, "socket_run(): epoll_wait: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
struct socket_t *s = events[i].data.ptr;
|
||||||
|
int state = s->socket_func(s->fd, !!(events[i].events & EPOLLOUT), !!(events[i].events & EPOLLIN), s->arg);
|
||||||
|
if (!state)
|
||||||
|
{
|
||||||
|
close(s->fd);
|
||||||
|
s->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* USE_POLL */
|
||||||
|
|
||||||
|
void *socket_run(void *arg)
|
||||||
|
{
|
||||||
|
s_running = 1;
|
||||||
|
|
||||||
|
while (s_running)
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
int n;
|
||||||
|
fd_set read_fd, write_fd;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
FD_ZERO(&read_fd);
|
||||||
|
FD_ZERO(&write_fd);
|
||||||
|
|
||||||
|
/* Add service sockets */
|
||||||
|
for (i = 0; i < s_sockets.used; i++)
|
||||||
|
{
|
||||||
|
int fd = s_sockets.items[i]->fd;
|
||||||
|
if (fd >= 0)
|
||||||
|
{
|
||||||
|
if (s_sockets.items[i]->mode & SM_READ ) FD_SET(fd, &read_fd);
|
||||||
|
if (s_sockets.items[i]->mode & SM_WRITE) FD_SET(fd, &write_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tv.tv_sec = 10;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
|
||||||
|
if (n == -1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "select: %s\n", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (n == 0) continue;
|
||||||
|
|
||||||
|
for (i = 0; i < s_sockets.used; i++)
|
||||||
|
{
|
||||||
|
struct socket_t *s = s_sockets.items[i];
|
||||||
|
int can_write = FD_ISSET(s->fd, &write_fd);
|
||||||
|
int can_read = FD_ISSET(s->fd, &read_fd);
|
||||||
|
|
||||||
|
if (can_write || can_read)
|
||||||
|
{
|
||||||
|
int state = s->socket_func(s->fd, can_write, can_read, s->arg);
|
||||||
|
if (!state)
|
||||||
|
{
|
||||||
|
close(s->fd);
|
||||||
|
s->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Socket thread finished\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* USE_POLL */
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef SOCKET_H
|
||||||
|
#define SOCKET_H
|
||||||
|
|
||||||
|
extern int s_running;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SM_READ = 1,
|
||||||
|
SM_WRITE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int(*socket_func)(int fd, int can_write, int can_read, void *arg);
|
||||||
|
|
||||||
|
void register_socket(int fd, int mode, socket_func socket_func, void *arg);
|
||||||
|
void deregister_socket(int fd);
|
||||||
|
|
||||||
|
void *socket_run(void *arg);
|
||||||
|
|
||||||
|
void socket_init(void);
|
||||||
|
|
||||||
|
void socket_set_nonblock(int fd);
|
||||||
|
void socket_set_nodelay(int fd);
|
||||||
|
|
||||||
|
void socket_flag_write(int fd);
|
||||||
|
void socket_clear_write(int fd);
|
||||||
|
|
||||||
|
#endif /* SOCKET_H */
|
Binary file not shown.
|
@ -0,0 +1,32 @@
|
||||||
|
#include <GL/glfw.h>
|
||||||
|
#include <FTGL/ftgl.h>
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
|
void drawText(const char *text, FTGLfont *font, float x, float y, float xs, float ys, int xa, int ya)
|
||||||
|
{
|
||||||
|
float bounds[6];
|
||||||
|
ftglGetFontBBox(font, text, -1, bounds);
|
||||||
|
int cx = bounds[3] - bounds[0];
|
||||||
|
int cy = bounds[4] - bounds[1];
|
||||||
|
|
||||||
|
float ox = 0, oy = 0;
|
||||||
|
switch (xa)
|
||||||
|
{
|
||||||
|
case TA_LEFT: ox = 0; break;
|
||||||
|
case TA_RIGHT: ox = -1; break;
|
||||||
|
case TA_CENTRE: ox = -0.5; break;
|
||||||
|
}
|
||||||
|
switch (ya)
|
||||||
|
{
|
||||||
|
case TA_TOP: oy = -1; break;
|
||||||
|
case TA_BOTTOM: oy = 0; break;
|
||||||
|
case TA_CENTRE: oy = -0.5; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(x, y, 0.0f);
|
||||||
|
glScalef(xs / TEXT_SIZE, ys / TEXT_SIZE, 1.0f);
|
||||||
|
glTranslatef(cx * ox, cy * oy, 0.0f);
|
||||||
|
ftglRenderFont(font, text, FTGL_RENDER_ALL);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef TEXT_H
|
||||||
|
#define TEXT_H
|
||||||
|
|
||||||
|
#define TEXT_SIZE 64
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TA_LEFT,
|
||||||
|
TA_RIGHT,
|
||||||
|
TA_TOP,
|
||||||
|
TA_BOTTOM,
|
||||||
|
TA_CENTRE,
|
||||||
|
};
|
||||||
|
|
||||||
|
void drawText(const char *text, FTGLfont *font, float x, float y, float xs, float ys, int xa, int ya);
|
||||||
|
|
||||||
|
#endif /* TEXT_H */
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "list.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "gettime.h"
|
||||||
|
|
||||||
|
struct timer_t
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
unsigned interval;
|
||||||
|
timer_func_t timer_func;
|
||||||
|
void *arg;
|
||||||
|
|
||||||
|
unsigned next_trigger;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int timer_t_compare(struct timer_t **a, struct timer_t **b)
|
||||||
|
{
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST(timer, struct timer_t *, timer_t_compare)
|
||||||
|
static struct timer_list_t s_timers;
|
||||||
|
static pthread_mutex_t s_timers_mutex;
|
||||||
|
|
||||||
|
struct timer_t *register_timer(const char *name, unsigned interval, timer_func_t timer_func, void *arg, int wait)
|
||||||
|
{
|
||||||
|
struct timer_t *t = malloc(sizeof *t);
|
||||||
|
t->name = strdup(name);
|
||||||
|
t->interval = interval;
|
||||||
|
t->timer_func = timer_func;
|
||||||
|
t->arg = arg;
|
||||||
|
|
||||||
|
/* Make timer run on next tick */
|
||||||
|
t->next_trigger = gettime() + (wait ? t->interval : 0);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_timers_mutex);
|
||||||
|
timer_list_add(&s_timers, t);
|
||||||
|
pthread_mutex_unlock(&s_timers_mutex);
|
||||||
|
|
||||||
|
fprintf(stderr, "Registered %s timer with %u ms interval\n", name, interval);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deregister_timer(struct timer_t *t)
|
||||||
|
{
|
||||||
|
timer_list_del_item(&s_timers, t);
|
||||||
|
|
||||||
|
fprintf(stderr, "Deregistered %s timer\n", t->name);
|
||||||
|
|
||||||
|
free(t->name);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timers_deinit(void)
|
||||||
|
{
|
||||||
|
while (s_timers.used > 0)
|
||||||
|
{
|
||||||
|
deregister_timer(s_timers.items[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_list_free(&s_timers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_timers(unsigned tick)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < s_timers.used; i++)
|
||||||
|
{
|
||||||
|
struct timer_t *t = s_timers.items[i];
|
||||||
|
if (tick >= t->next_trigger)
|
||||||
|
{
|
||||||
|
t->next_trigger = tick + t->interval;
|
||||||
|
t->timer_func(t->arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_set_interval(struct timer_t *t, unsigned interval)
|
||||||
|
{
|
||||||
|
/* Adjust next triggering */
|
||||||
|
t->next_trigger += (interval - t->interval);
|
||||||
|
t->interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_set_interval_by_name(const char *name, unsigned interval)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < s_timers.used; i++)
|
||||||
|
{
|
||||||
|
struct timer_t *t = s_timers.items[i];
|
||||||
|
if (strcmp(t->name, name) == 0)
|
||||||
|
{
|
||||||
|
timer_set_interval(t, interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
typedef void(*timer_func_t)(void *arg);
|
||||||
|
|
||||||
|
struct timer_t;
|
||||||
|
|
||||||
|
struct timer_t *register_timer(const char *name, unsigned interval, timer_func_t timer_func, void *arg, int wait);
|
||||||
|
void deregister_timer(struct timer_t *handle);
|
||||||
|
void process_timers(unsigned tick);
|
||||||
|
|
||||||
|
void timer_set_interval(struct timer_t *t, unsigned interval);
|
||||||
|
void timer_set_interval_by_name(const char *name, unsigned interval);
|
||||||
|
|
||||||
|
#endif /* TIMER_H */
|
|
@ -0,0 +1,133 @@
|
||||||
|
#ifdef WIN32
|
||||||
|
//#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
//#include <sys/syscall.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "queue.h"
|
||||||
|
#include "worker.h"
|
||||||
|
//#include "mcc.h"
|
||||||
|
#include "gettime.h"
|
||||||
|
|
||||||
|
void *worker_thread(void *arg)
|
||||||
|
{
|
||||||
|
struct worker *worker = arg;
|
||||||
|
|
||||||
|
bool timeout = false;
|
||||||
|
int jobs = 0;
|
||||||
|
//pid_t tid = (pid_t)syscall(SYS_gettid);
|
||||||
|
unsigned tid = 0;
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
nice(worker->nice);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fprintf(stderr, "Queue worker %s thread (%u) started with nice %d\n", worker->name, tid, worker->nice);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
ts.tv_sec += worker->timeout;
|
||||||
|
|
||||||
|
int s = sem_timedwait(&worker->sem, &ts);
|
||||||
|
if (s == -1)
|
||||||
|
{
|
||||||
|
worker->thread_timeout = true;
|
||||||
|
if (errno == ETIMEDOUT) {
|
||||||
|
timeout = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Queue worker %s thread (%u): %s\n", worker->name, tid, strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
if (queue_consume(worker->queue, &data))
|
||||||
|
{
|
||||||
|
/* Null item added to the queue indicate we should exit. */
|
||||||
|
if (data == NULL) break;
|
||||||
|
|
||||||
|
worker->callback(data);
|
||||||
|
jobs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Queue worker %s thread (%u) exiting after %d jobs due to timeout\n", worker->name, tid, jobs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Queue worker %s thread (%u) exiting after %d jobs\n", worker->name, tid, jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_init(struct worker *worker, const char *name, unsigned timeout, int nice, worker_callback callback)
|
||||||
|
{
|
||||||
|
memset(worker, 0, sizeof *worker);
|
||||||
|
|
||||||
|
strncpy(worker->name, name, sizeof worker->name);
|
||||||
|
worker->thread_valid = false;
|
||||||
|
worker->thread_timeout = false;
|
||||||
|
worker->timeout = timeout / 1000;
|
||||||
|
worker->nice = nice;
|
||||||
|
|
||||||
|
worker->queue = queue_new();
|
||||||
|
worker->callback = callback;
|
||||||
|
sem_init(&worker->sem, 0, 0);
|
||||||
|
|
||||||
|
fprintf(stderr, "Queue worker %s initialised\n", worker->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_deinit(struct worker *worker)
|
||||||
|
{
|
||||||
|
if (worker->thread_valid)
|
||||||
|
{
|
||||||
|
if (!worker->thread_timeout)
|
||||||
|
{
|
||||||
|
if (queue_produce(worker->queue, NULL))
|
||||||
|
{
|
||||||
|
sem_post(&worker->sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_join(worker->thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_delete(worker->queue);
|
||||||
|
|
||||||
|
fprintf(stderr, "Queue worker %s deinitialised\n", worker->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_queue(struct worker *worker, void *data)
|
||||||
|
{
|
||||||
|
if (worker->thread_timeout)
|
||||||
|
{
|
||||||
|
pthread_join(worker->thread, NULL);
|
||||||
|
worker->thread_timeout = false;
|
||||||
|
worker->thread_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!worker->thread_valid)
|
||||||
|
{
|
||||||
|
worker->thread_valid = (pthread_create(&worker->thread, NULL, &worker_thread, worker) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue_produce(worker->queue, data))
|
||||||
|
{
|
||||||
|
sem_post(&worker->sem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Queue worker %s unable to queue\n", worker->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef WORKER_H
|
||||||
|
#define WORKER_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
typedef void(*worker_callback)(void *arg);
|
||||||
|
|
||||||
|
struct worker
|
||||||
|
{
|
||||||
|
char name[16];
|
||||||
|
int thread_valid;
|
||||||
|
int thread_timeout;
|
||||||
|
unsigned timeout;
|
||||||
|
int nice;
|
||||||
|
|
||||||
|
struct queue_t *queue;
|
||||||
|
pthread_t thread;
|
||||||
|
worker_callback callback;
|
||||||
|
sem_t sem;
|
||||||
|
};
|
||||||
|
|
||||||
|
void worker_init(struct worker *worker, const char *name, unsigned timeout, int nice, worker_callback callback);
|
||||||
|
void worker_deinit(struct worker *worker);
|
||||||
|
void worker_queue(struct worker* worker, void *data);
|
||||||
|
|
||||||
|
#endif /* WORKER_H */
|
Loading…
Reference in New Issue