hexagram/examples/cluster.c
2019-06-07 00:06:35 -05:00

496 lines
12 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <hexagram/can.h>
#include <hexagram/window.h>
typedef struct _hexagram_cluster_state {
double rpm, rps, temp, level;
} hexagram_cluster_state;
static hexagram_cluster_state state = {
0, 0, 0, 0
};
static void draw_needle(cairo_t *cr,
double x,
double y,
double r,
double min_angle,
double max_angle,
double value) {
double angle = min_angle + ((max_angle - min_angle) * value) - (M_PI/2);
cairo_move_to(cr, x, y);
cairo_set_source_rgb(cr, 0, 0.25, 1.0);
cairo_line_to(cr,
x + r * cos(angle),
y + r * sin(angle));
cairo_stroke(cr);
}
static void draw_face_number(cairo_t *cr,
double x,
double y,
double r,
double min_angle,
double max_angle,
double value,
const char *text) {
double angle = min_angle + ((max_angle - min_angle) * value) - 1.658;
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_move_to(cr,
x + r * cos(angle),
y + r * sin(angle));
cairo_save(cr);
cairo_rotate(cr, angle + 1.658);
cairo_show_text(cr, text);
cairo_stroke(cr);
cairo_restore(cr);
}
static void draw_tachometer_needle(cairo_t *cr,
double x,
double y,
double r,
double rpm) {
if (rpm > 8000) {
rpm = 8000;
}
/*
* Draw a tiny boi circle
*/
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, 0.08 * r, 0, 2*M_PI);
cairo_fill(cr);
draw_needle(cr, x, y, 0.77 * r,
232 * (M_PI/180),
488 * (M_PI/180),
rpm / 8000);
}
static void draw_tachometer(cairo_t *cr,
double x,
double y,
double r) {
int i;
cairo_select_font_face(cr, "Helvetica",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, r * 0.125);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, r, 0, 2*M_PI);
cairo_stroke(cr);
/*
* Draw face numbers
*/
for (i=0; i<=80; i+=10) {
char text[4];
snprintf(text, 3, "%02d", i);
draw_face_number(cr, x, y,
0.85 * r,
232 * (M_PI/180),
488 * (M_PI/180),
i / 80.0,
text);
}
}
static void draw_speedometer(cairo_t *cr,
double x,
double y,
double r) {
int i;
cairo_select_font_face(cr, "Helvetica",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, r * 0.125);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, r, 0, 2*M_PI);
cairo_stroke(cr);
/*
* Draw face numbers
*/
for (i=0; i<=180; i+=20) {
char text[5];
snprintf(text, 4, "%02d", i);
draw_face_number(cr, x, y,
0.85 * r,
232 * (M_PI/180),
488 * (M_PI/180),
i / 180.0,
text);
}
}
static void draw_speedometer_needle(cairo_t *cr,
double x,
double y,
double r,
double kph) {
if (kph > 290) {
kph = 290;
}
/*
* Draw a tiny boi circle
*/
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, 0.08 * r, 0, 2*M_PI);
cairo_fill(cr);
draw_needle(cr, x, y, 0.77 * r,
232 * (M_PI/180),
488 * (M_PI/180),
kph / 290);
}
static void draw_thermometer(cairo_t *cr,
double x,
double y,
double r) {
int i;
cairo_select_font_face(cr, "Helvetica",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, r * 0.15);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, r, 0, 2*M_PI);
cairo_stroke(cr);
/*
* Draw face numbers
*/
for (i=120; i<=260; i+=70) {
char text[5];
snprintf(text, 4, "%02d", i);
draw_face_number(cr, x, y,
0.75 * r,
300 * (M_PI/180),
420 * (M_PI/180),
(i - 120.0) / 140.0,
text);
}
}
static void draw_thermometer_needle(cairo_t *cr,
double x,
double y,
double r,
double temp) {
if (temp > 260) {
temp = 260;
}
/*
* Draw a tiny boi circle
*/
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, 0.08 * r, 0, 2*M_PI);
cairo_fill(cr);
draw_needle(cr, x, y, 0.77 * r,
300 * (M_PI/180),
420 * (M_PI/180),
temp / 260);
}
static void draw_fuel_gauge(cairo_t *cr,
double x,
double y,
double r) {
cairo_arc(cr, x, y, r, 0, 2*M_PI);
cairo_stroke(cr);
}
static void draw_fuel_gauge_needle(cairo_t *cr,
double x,
double y,
double r,
double level) {
if (level > 1.0) {
level = 1.0;
}
/*
* Draw a tiny boi circle
*/
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_arc(cr, x, y, 0.08 * r, 0, 2*M_PI);
cairo_fill(cr);
draw_needle(cr, x, y, 0.77 * r,
300 * (M_PI/180),
420 * (M_PI/180),
level / 1.0);
}
static void draw_mfd(cairo_t *cr,
double x,
double y,
double width,
double height) {
cairo_set_source_rgb(cr, 0.75, 0, 0);
cairo_move_to(cr, x, y);
cairo_line_to(cr, x + width, y);
cairo_line_to(cr, x + width, y + height);
cairo_line_to(cr, x, y + height);
cairo_line_to(cr, x, y);
cairo_fill(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_move_to(cr, x, y);
cairo_line_to(cr, x + width, y);
cairo_line_to(cr, x + width, y + height);
cairo_line_to(cr, x, y + height);
cairo_line_to(cr, x, y);
cairo_stroke(cr);
}
static void cluster_draw_bg(cairo_t *cr,
double width,
double height) {
/*
* Paint canvas black
*/
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);
draw_tachometer(cr,
width * 0.2,
height * 0.5,
height * 0.4);
draw_speedometer(cr,
width * 0.8,
height * 0.5,
height * 0.4);
draw_thermometer(cr,
width * 0.43,
height * 0.76,
height * 0.13);
draw_fuel_gauge(cr,
width * 0.57,
height * 0.76,
height * 0.13);
draw_mfd(cr,
width * 0.42,
height * 0.1,
width * 0.16,
height * 0.5);
}
static void cluster_update(struct can_frame *frame) {
switch (frame->can_id) {
case 0x280: {
state.rpm = 0.25 *
(double)(frame->data[2] | (frame->data[3] << 8));
break;
}
case 0x288: {
state.temp = (double)(frame->data[1] - 48 * 0.75);
break;
}
case 0x320: {
state.level = (double)(frame->data[2]);
break;
}
case 0x5a0: {
state.rps = 0.001 * (double)((frame->data[1] >> 1)
| (frame->data[2] << 8));
break;
}
}
}
static void cluster_draw(cairo_t *cr,
double x,
double y,
double width,
double height) {
draw_tachometer_needle(cr, x + width * 0.2,
y + height * 0.5,
height * 0.4,
state.rpm);
draw_speedometer_needle(cr,
width * 0.8,
height * 0.5,
height * 0.4,
(2.032 * state.rps * 3600) / 1000.0);
draw_thermometer_needle(cr,
width * 0.43,
height * 0.76,
height * 0.11,
state.temp);
draw_fuel_gauge_needle(cr,
width * 0.57,
height * 0.76,
height * 0.11,
0.5);
}
int main(int argc, char **argv) {
Display *display;
fd_set rfds, rready;
hexagram_can_if *can_if;
hexagram_window *window;
int fd, fd2,
width = 1024,
height = 480;
struct timeval now,
last = {
.tv_sec = 0,
.tv_usec = 0
};
struct timeval timeout = {
.tv_sec = 0,
.tv_usec = 250000
};
cairo_t *fg, *bg;
if ((can_if = hexagram_can_if_open("vcan0")) == NULL)
exit(1);
if ((window = hexagram_window_new_x11(NULL, width, height)) == NULL)
exit(1);
display = hexagram_window_display(window);
fd = hexagram_can_if_fd(can_if);
fd2 = hexagram_window_display_fd(window);
/*
* Set up the rendering surfaces
*/
fg = hexagram_window_create_fg_context(window);
bg = hexagram_window_create_bg_context(window);
/*
* Draw the background layer
*/
cluster_draw_bg(bg, width, height);
/*
* Present the background layer
*/
hexagram_window_refresh_bg(window);
hexagram_window_swap_buffer(window);
hexagram_window_show(window);
/*
* Set up file descriptors to monitor
*/
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_SET(fd2, &rfds);
while (1) {
int pending;
(void)gettimeofday(&now, NULL);
while ((pending = XPending(display)) != 0) {
XEvent e;
XNextEvent(display, &e);
switch (e.type) {
case MapNotify:
case Expose:
hexagram_window_refresh_bg(window);
hexagram_window_swap_buffer(window);
break;
case ButtonPress:
case KeyPress:
break;
default:
fprintf(stderr, "Dropping unhandled XEvent.type = %d.\n", e.type);
}
}
memcpy(&rready, &rfds, sizeof(rfds));
if (select(fd2, &rready, NULL, NULL, &timeout) < 0) {
break;
}
if (FD_ISSET(fd, &rready)) {
struct can_frame frame;
hexagram_can_if_read(can_if, &frame);
switch (frame.can_id) {
case 0x280:
case 0x288:
case 0x320:
case 0x5a0: {
cluster_update(&frame);
if (now.tv_sec - last.tv_sec
|| now.tv_usec - last.tv_usec > 16666) {
hexagram_window_refresh_bg(window);
cluster_draw(fg, 0, 0, width, height);
hexagram_window_swap_buffer(window);
(void)memcpy(&last, &now, sizeof(now));
}
}
}
}
}
hexagram_window_destroy(window);
return 0;
}