#include #include #include #include #include #include #include #include #include #include #include #include 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) { cairo_arc(cr, x, y, r, 0, 2*M_PI); cairo_stroke(cr); } static void draw_thermometer(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(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_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 draw_gauge_cluster_bg(hexagram_window *window, double width, double height) { cairo_t *cr; cr = hexagram_window_create_bg_context(window); /* * 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); cairo_destroy(cr); } static void update_gauge_cluster(cairo_t *cr, struct can_frame *frame, double x, double y, double width, double height) { static double rpm = 0; if (frame->can_id == 0x280) { rpm = 0.25 * (double)(frame->data[2] | (frame->data[3] << 8)); } else { return; } draw_tachometer_needle(cr, x + width * 0.2, y + height * 0.5, height * 0.4, rpm); } 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; cairo_surface_t *sfc; cairo_t *cr; if ((can_if = hexagram_can_if_open("vcan0")) == NULL) exit(1); if ((window = hexagram_window_new_x11(NULL, width, height)) == NULL) exit(1); display = window->display; fd = hexagram_can_if_fd(can_if); fd2 = hexagram_window_display_fd(window); /* * Draw the background layer */ draw_gauge_cluster_bg(window, width, height); hexagram_window_show(window); /* * Set up the foreground surface */ sfc = window->surface_fg; cr = cairo_create(sfc); /* * Set up file descriptors to monitor */ FD_ZERO(&rfds); FD_SET(fd, &rfds); FD_SET(fd2, &rfds); while (1) { int pending; while ((pending = XPending(display)) != 0) { XEvent e; XNextEvent(display, &e); switch (e.type) { case Expose: fprintf(stderr, "Event %d\n", e.type); hexagram_window_refresh_bg(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, NULL) < 0) { break; } if (FD_ISSET(fd, &rready)) { struct can_frame frame; hexagram_can_if_read(can_if, &frame); if (frame.can_id == 0x280) { hexagram_window_refresh_bg(window); update_gauge_cluster(cr, &frame, 0, 0, width, height); hexagram_window_swap_buffer(window); } } } cairo_destroy(cr); cairo_surface_destroy(sfc); XCloseDisplay(display); return 0; }