diff --git a/examples/Makefile b/examples/Makefile index fe3f13d..78637b9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -15,10 +15,10 @@ RM = rm all: $(EXAMPLES) cluster: cluster.c $(STATIC) - $(CC) $(CFLAGS) $< -o $@ $(STATIC) $(shell pkg-config --cflags --libs cairo x11) -lXext -lm + $(CC) $(CFLAGS) $< -o $@ $(STATIC) $(shell pkg-config --cflags --libs cairo x11 librsvg-2.0) -lXext -lm svg: svg.c $(STATIC) - $(CC) $(CFLAGS) $< -o $@ $(STATIC) $(shell pkg-config --cflags --libs cairo) -lm + $(CC) $(CFLAGS) $< -o $@ $(STATIC) $(shell pkg-config --cflags --libs cairo librsvg-2.0) -lm view: view.c $(STATIC) $(CC) $(CFLAGS) $< -o $@ $(STATIC) -lncurses diff --git a/examples/cluster.c b/examples/cluster.c index d80f11a..3cb303a 100644 --- a/examples/cluster.c +++ b/examples/cluster.c @@ -45,9 +45,9 @@ static void cluster_update(hexagram_cluster *cluster, struct timeval *last) { struct timeval now; - if (!hexagram_cluster_update(cluster, frame)) { - return; - } + //if (!hexagram_cluster_update(cluster, frame)) { + // return; + //} gettimeofday(&now, NULL); @@ -84,7 +84,7 @@ int main(int argc, char **argv) { hexagram_cluster *cluster; int fd, fd2, - width = 1024, + width = 1280, height = 480; cairo_t *fg, *bg; @@ -110,7 +110,7 @@ int main(int argc, char **argv) { return 1; } - if ((cluster = hexagram_cluster_new(width, height)) == NULL) { + if ((cluster = hexagram_cluster_new(1200, 6500, 8000)) == NULL) { perror("hexagram_cluster_new()"); return 1; diff --git a/examples/svg.c b/examples/svg.c index 887ad46..7b2219b 100644 --- a/examples/svg.c +++ b/examples/svg.c @@ -8,7 +8,47 @@ #include -#include +#include + +static char *path_data = + "M 23.246093 0," + " 0 13.382812," + "V 44.689453" + "L 0.0839844 44.640625," + " 24.033203 58.505859," + " 24.001953 86.332031," + " 22.841796 87" + "h 4.478516" + "L 26.068359 86.275391," + " 26.099609 58.449219," + " 50.083984 44.640625," + " 74.033203 58.505859," + " 74.001953 86.332031," + " 72.841796 87" + "h 4.478516" + "L 76.068359 86.275391," + " 76.099609 58.449219," + " 100 44.689453," + "V 13.365234" + "L 76.919921 0" + "H 73.246093" + "L 50.015625 13.373047," + " 26.919921 0" + "Z" + "M 25.083984 1.25," + " 49.033203 15.115234," + " 49.001953, 42.941406," + " 25.017578 56.75," + " 1.0019531 42.845703," + "l 0.033203 -27.75" + "z" + "m 50 0," + " 24.017576 13.904297," + " -0.0352 27.75" + "L 75.017578 56.75," + " 51.068359 42.884766," + " 51.099609 15.058594" + "Z"; static int usage(int argc, char **argv, const char *message, ...) { if (message) { @@ -26,9 +66,7 @@ static int usage(int argc, char **argv, const char *message, ...) { } int main(int argc, char **argv) { - hexagram_cluster *cluster; - - int width = 1024, + int width = 1280, height = 480; cairo_surface_t *surface; @@ -39,10 +77,6 @@ int main(int argc, char **argv) { return usage(argc, argv, "No output SVG file specified"); } - if ((cluster = hexagram_cluster_new(width, height)) == NULL) { - return 1; - } - surface = cairo_svg_surface_create(argv[1], width, height); /* @@ -53,11 +87,19 @@ int main(int argc, char **argv) { /* * Draw the background layer */ - hexagram_cluster_draw_bg(cluster, bg); + if (hexagram_path_run(bg, path_data, strlen(path_data)) < 0) { + printf("Oh noes!\n"); + goto error_path_run; + } + + cairo_stroke(bg); cairo_destroy(bg); cairo_surface_destroy(surface); return 0; + +error_path_run: + return -1; } diff --git a/include/hexagram/clock.h b/include/hexagram/clock.h new file mode 100644 index 0000000..6e00448 --- /dev/null +++ b/include/hexagram/clock.h @@ -0,0 +1,32 @@ +#ifndef _HEXAGRAM_CLOCK_H +#define _HEXAGRAM_CLOCK_H + +#include +#include + +#include + +typedef enum { + HEXAGRAM_CLOCK_24H, + HEXAGRAM_CLOCK_12H +} hexagram_clock_format; + +typedef struct _hexagram_clock_time { + uint16_t hour, minute; +} hexagram_clock_time; + +typedef struct _hexagram_clock { + hexagram_text text; + hexagram_clock_format format; + hexagram_clock_time time; +} hexagram_clock; + +int hexagram_clock_init(hexagram_clock *clock, + double x, + double y, + hexagram_text_align align, + hexagram_clock_format format); + +void hexagram_clock_set(hexagram_clock *clock, time_t now); + +#endif /* _HEXAGRAM_CLOCK_H */ diff --git a/include/hexagram/cluster.h b/include/hexagram/cluster.h index ca9e83f..1ec4a85 100644 --- a/include/hexagram/cluster.h +++ b/include/hexagram/cluster.h @@ -3,48 +3,56 @@ #include +#include #include -#include +#include #include -#include #include -#include +#include +#include +#include +#include +#include +#include + +#define HEXAGRAM_CLUSTER_GAUGE_COUNT 9 typedef struct _hexagram_cluster { - hexagram_tacho tacho; - hexagram_speedo speedo; - hexagram_thermo thermo; - hexagram_fuel fuel; - hexagram_mfd mfd; - double width, height; - struct { - double rpm, rps, temp, fuel; - } state; + hexagram_pattern pattern; + + hexagram_speedo speedo; + hexagram_fuel fuel; + hexagram_tacho tacho; + hexagram_thermo thermo; + hexagram_odo odo; + hexagram_temp temp; + hexagram_clock clock; + hexagram_status_icon_box status; + hexagram_tacho_shift_indicator shift_indicator; + + hexagram_gauge *gauges[HEXAGRAM_CLUSTER_GAUGE_COUNT]; } hexagram_cluster; -void hexagram_cluster_resize(hexagram_cluster *cluster, - double width, - double height); +int hexagram_cluster_init(hexagram_cluster *cluster, + double rpm_min, + double rpm_redline, + double rpm_max); -void hexagram_cluster_init(hexagram_cluster *cluster, - double width, - double height); +void hexagram_cluster_cleanup(hexagram_cluster *cluster); -hexagram_cluster *hexagram_cluster_new(double width, - double height); +hexagram_cluster *hexagram_cluster_new(double rpm_min, + double rpm_redline, + double rpm_max); + +void hexagram_cluster_destroy(hexagram_cluster *cluster); int hexagram_cluster_filter_can_if(hexagram_can_if *can_if); -int hexagram_cluster_update(hexagram_cluster *cluster, - struct can_frame *frame); +void hexagram_cluster_draw_bg(hexagram_cluster *cluster, cairo_t *cr); -void hexagram_cluster_draw_bg(hexagram_cluster *cluster, - cairo_t *cr); - -void hexagram_cluster_draw_fg(hexagram_cluster *cluster, - cairo_t *cr); +void hexagram_cluster_draw_fg(hexagram_cluster *cluster, cairo_t *cr); #endif /* _HEXAGRAM_CLUSTER_H */ diff --git a/include/hexagram/dial.h b/include/hexagram/dial.h new file mode 100644 index 0000000..f0e7dfc --- /dev/null +++ b/include/hexagram/dial.h @@ -0,0 +1,73 @@ +#ifndef _HEXAGRAM_DIAL_H +#define _HEXAGRAM_DIAL_H + +#include + +typedef enum { + HEXAGRAM_DIAL_MAIN, + HEXAGRAM_DIAL_BOTTOM +} hexagram_dial_type; + +typedef struct _hexagram_dial { + hexagram_gauge gauge; + hexagram_dial_type type; + + double radius, + min_angle, + max_angle, + min_value, + max_value; + + double needle_min_radius, + needle_max_radius; + + double mark_min_radius, + mark_max_radius; + + double value; + cairo_pattern_t *gradient; +} hexagram_dial; + +int hexagram_dial_init(hexagram_dial *dial, + hexagram_dial_type type, + double x, + double y, + double radius, + double min_angle, + double max_angle, + double min_value, + double max_value); + +double hexagram_dial_angle(hexagram_dial *dial, double value); + +int hexagram_dial_draw_legend(hexagram_dial *dial, + cairo_t *cr, + double radius, + double value, + char *text); + +int hexagram_dial_draw_legend_offset(hexagram_dial *dial, + cairo_t *cr, + double radius, + double value, + char *text, + double x_offset, + double y_offset); + +int hexagram_dial_draw_mark(hexagram_dial *dial, + cairo_t *cr, + double min_radius, + double max_radius, + double value); + +int hexagram_dial_draw_needle(hexagram_dial *dial, + cairo_t *cr, + double min_radius, + double max_radius, + double value); + +int hexagram_dial_draw_bg(hexagram_dial *dial, cairo_t *cr); + +int hexagram_dial_draw_fg(hexagram_dial *dial, cairo_t *cr); + +#endif /* _HEXAGRAM_DIAL_H */ diff --git a/include/hexagram/fuel.h b/include/hexagram/fuel.h index 03d2037..e860a06 100644 --- a/include/hexagram/fuel.h +++ b/include/hexagram/fuel.h @@ -1,26 +1,16 @@ #ifndef _HEXAGRAM_FUEL_H #define _HEXAGRAM_FUEL_H -#include - -#include +#include typedef struct _hexagram_fuel { - hexagram_gauge gauge; - double redline; + hexagram_dial dial; + double warn_level; } hexagram_fuel; -void hexagram_fuel_init(hexagram_fuel *fuel, - double x, - double y, - double radius, - double redline); - -void hexagram_fuel_draw_face(hexagram_fuel *fuel, - cairo_t *cr); - -void hexagram_fuel_draw_needle(hexagram_fuel *fuel, - cairo_t *cr, - double level); +int hexagram_fuel_init(hexagram_fuel *fuel, + double x, + double y, + double radius); #endif /* _HEXAGRAM_FUEL_H */ diff --git a/include/hexagram/gauge.h b/include/hexagram/gauge.h index 12ad1e0..964d49d 100644 --- a/include/hexagram/gauge.h +++ b/include/hexagram/gauge.h @@ -3,35 +3,26 @@ #include +typedef struct _hexagram_gauge hexagram_gauge; + +typedef int (hexagram_gauge_draw_callback)(hexagram_gauge *, + cairo_t *); + typedef struct _hexagram_gauge { double x, y; - double radius; - double min_angle; - double max_angle; + + hexagram_gauge_draw_callback *draw_bg, + *draw_fg; } hexagram_gauge; void hexagram_gauge_init(hexagram_gauge *gauge, double x, double y, - double radius, - double min_angle, - double max_angle); + hexagram_gauge_draw_callback *draw_bg, + hexagram_gauge_draw_callback *draw_fg); -void hexagram_gauge_draw_number(hexagram_gauge *gauge, - cairo_t *cr, - double radius, - double value, - const char *text); +int hexagram_gauge_draw_fg(hexagram_gauge *gauge, cairo_t *cr); -void hexagram_gauge_draw_mark(hexagram_gauge *gauge, - cairo_t *cr, - double min_radius, - double max_radius, - double value); - -void hexagram_gauge_draw_needle(hexagram_gauge *gauge, - cairo_t *cr, - double radius, - double value); +int hexagram_gauge_draw_bg(hexagram_gauge *gauge, cairo_t *cr); #endif /* _HEXAGRAM_GAUGE_H */ diff --git a/include/hexagram/icon.h b/include/hexagram/icon.h new file mode 100644 index 0000000..a784178 --- /dev/null +++ b/include/hexagram/icon.h @@ -0,0 +1,47 @@ +#ifndef _HEXAGRAM_ICON_H +#define _HEXAGRAM_ICON_H + +#include +#include + +#define HEXAGRAM_ICON_COLOR_COUNT 6 + +typedef enum { + HEXAGRAM_ICON_COLOR_RED, + HEXAGRAM_ICON_COLOR_ORANGE, + HEXAGRAM_ICON_COLOR_YELLOW, + HEXAGRAM_ICON_COLOR_GREEN, + HEXAGRAM_ICON_COLOR_BLUE, + HEXAGRAM_ICON_COLOR_WHITE +} hexagram_icon_color; + +typedef struct _hexagram_icon { + int id; + const char *name, + *style_format; + + union { + struct { + int red, orange, yellow, green, blue, white; + }; + + int colors[HEXAGRAM_ICON_COLOR_COUNT]; + }; + + double width, height; + cairo_surface_t *surfaces[HEXAGRAM_ICON_COLOR_COUNT]; +} hexagram_icon; + +int hexagram_icon_init(hexagram_icon *icon, double width, double height); + +void hexagram_icon_cleanup(hexagram_icon *icon); + +int hexagram_icon_drawable(hexagram_icon *icon, int status); + +int hexagram_icon_draw(hexagram_icon *icon, + cairo_t *cr, + double x, + double y, + int status); + +#endif /* _HEXAGRAM_ICON_H */ diff --git a/include/hexagram/odo.h b/include/hexagram/odo.h new file mode 100644 index 0000000..4b690c6 --- /dev/null +++ b/include/hexagram/odo.h @@ -0,0 +1,23 @@ +#ifndef _HEXAGRAM_ODO_H +#define _HEXAGRAM_ODO_H + +#include + +typedef enum { + HEXAGRAM_ODO_KILOMETERS, + HEXAGRAM_ODO_MILES +} hexagram_odo_units; + +typedef struct _hexagram_odo { + hexagram_text text; + hexagram_odo_units units; + double value; +} hexagram_odo; + +int hexagram_odo_init(hexagram_odo *odo, + double x, + double y, + hexagram_text_align align, + hexagram_odo_units units); + +#endif /* _HEXAGRAM_ODO_H */ diff --git a/include/hexagram/path.h b/include/hexagram/path.h new file mode 100644 index 0000000..ab07f36 --- /dev/null +++ b/include/hexagram/path.h @@ -0,0 +1,21 @@ +#ifndef _HEXAGRAM_PATH_H +#define _HEXAGRAM_PATH_H + +#include +#include + +typedef enum { + HEXAGRAM_PATH_STATE_NONE, + HEXAGRAM_PATH_STATE_COMMAND, + HEXAGRAM_PATH_STATE_ARG +} hexagram_path_state; + +typedef enum { + HEXAGRAM_PATH_ARG_NONE, + HEXAGRAM_PATH_ARG_INT, + HEXAGRAM_PATH_ARG_FLOAT +} hexagram_path_arg_type; + +int hexagram_path_run(cairo_t *cr, const char *data, size_t len); + +#endif /* _HEXAGRAM_PATH_H */ diff --git a/include/hexagram/pattern.h b/include/hexagram/pattern.h new file mode 100644 index 0000000..ab2888d --- /dev/null +++ b/include/hexagram/pattern.h @@ -0,0 +1,34 @@ +#ifndef _HEXAGRAM_PATTERN_H +#define _HEXAGRAM_PATTERN_H + +#include +#include + +typedef enum { + HEXAGRAM_PATTERN_STROKE, + HEXAGRAM_PATTERN_FILL +} hexagram_pattern_op; + +typedef struct _hexagram_pattern { + double width, height; + double color[3]; + + cairo_surface_t *surface; +} hexagram_pattern; + +int hexagram_pattern_init(hexagram_pattern *pattern, + double width, + double height, + const double color[3], + hexagram_pattern_op op, + const char *pathdata, + size_t len); + +void hexagram_pattern_cleanup(hexagram_pattern *pattern); + +void hexagram_pattern_fill(hexagram_pattern *pattern, + cairo_t *cr, + const double matrix[6], + const double scale[2]); + +#endif /* _HEXAGRAM_PATTERN_H */ diff --git a/include/hexagram/speedo.h b/include/hexagram/speedo.h index 5665840..a3b238d 100644 --- a/include/hexagram/speedo.h +++ b/include/hexagram/speedo.h @@ -1,27 +1,25 @@ #ifndef _HEXAGRAM_SPEEDO_H #define _HEXAGRAM_SPEEDO_H -#include +#include -#include +#include -#define HEXAGRAM_SPEEDO_MAX_KPH 289.682 -#define HEXAGRAM_SPEEDO_MAX_MPH 180.000 +typedef enum { + HEXAGRAM_SPEEDO_KPH, + HEXAGRAM_SPEEDO_MPH +} hexagram_speedo_units; typedef struct _hexagram_speedo { - hexagram_gauge gauge; + hexagram_dial dial; + hexagram_speedo_units units; } hexagram_speedo; -void hexagram_speedo_init(hexagram_speedo *speedo, - double x, - double y, - double radius); - -void hexagram_speedo_draw_face(hexagram_speedo *speedo, - cairo_t *cr); - -void hexagram_speedo_draw_needle(hexagram_speedo *speedo, - cairo_t *cr, - double kph); +int hexagram_speedo_init(hexagram_speedo *speedo, + double x, + double y, + double radius, + double max_value, + hexagram_speedo_units units); #endif /* _HEXAGRAM_SPEEDO_H */ diff --git a/include/hexagram/status.h b/include/hexagram/status.h new file mode 100644 index 0000000..6cab2c9 --- /dev/null +++ b/include/hexagram/status.h @@ -0,0 +1,173 @@ +#ifndef _HEXAGRAM_STATUS_H +#define _HEXAGRAM_STATUS_H + +#include + +#include +#include + +#define HEXAGRAM_STATUS_ICON_WIDTH 48 +#define HEXAGRAM_STATUS_ICON_HEIGHT 48 + +typedef enum { + HEXAGRAM_STATUS_CAUTION, + HEXAGRAM_STATUS_COLLISION, + HEXAGRAM_STATUS_FUEL, + HEXAGRAM_STATUS_SEATBELT, + HEXAGRAM_STATUS_COOLANT, + HEXAGRAM_STATUS_OIL, + HEXAGRAM_STATUS_PARKING_BRAKE, + HEXAGRAM_STATUS_WIPER_WASHER, + HEXAGRAM_STATUS_LANE_ASSIST, + HEXAGRAM_STATUS_STABILITY_CONTROL, + HEXAGRAM_STATUS_TRACTION_CONTROL, + HEXAGRAM_STATUS_TPMS, + HEXAGRAM_STATUS_ABS, + HEXAGRAM_STATUS_AIRBAG, + HEXAGRAM_STATUS_BATTERY, + HEXAGRAM_STATUS_ENGINE, + HEXAGRAM_STATUS_FOG_BEAM, + HEXAGRAM_STATUS_PARKING_BEAM, + HEXAGRAM_STATUS_LOW_BEAM, + HEXAGRAM_STATUS_HIGH_BEAM, + HEXAGRAM_STATUS_COLD, + HEXAGRAM_STATUS_COUNT +} hexagram_status_type; + +typedef enum { + HEXAGRAM_STATUS_CAUTION_OFF, + HEXAGRAM_STATUS_CAUTION_ON, + HEXAGRAM_STATUS_CAUTION_WARNING +} hexagram_status_caution; + +typedef enum { + HEXAGRAM_STATUS_COLLISION_OK, + HEXAGRAM_STATUS_COLLISION_DETECTED +} hexagram_status_collision; + +typedef enum { + HEXAGRAM_STATUS_FUEL_OK, + HEXAGRAM_STATUS_FUEL_LOW, + HEXAGRAM_STATUS_FUEL_FAULT +} hexagram_status_fuel; + +typedef enum { + HEXAGRAM_STATUS_SEATBELT_OK, + HEXAGRAM_STATUS_SEATBELT_UNFASTENED, + HEXAGRAM_STATUS_SEATBELT_FAULT +} hexagram_status_seatbelt; + +typedef enum { + HEXAGRAM_STATUS_COOLANT_OK, + HEXAGRAM_STATUS_COOLANT_LOW, + HEXAGRAM_STATUS_COOLANT_OVERHEAT, + HEXAGRAM_STATUS_COOLANT_FAULT +} hexagram_status_coolant; + +typedef enum { + HEXAGRAM_STATUS_OIL_OK, + HEXAGRAM_STATUS_OIL_LOW, + HEXAGRAM_STATUS_OIL_OVERHEAT, + HEXAGRAM_STATUS_OIL_FAULT +} hexagram_status_oil; + +typedef enum { + HEXAGRAM_STATUS_PARKING_BRAKE_OFF, + HEXAGRAM_STATUS_PARKING_BRAKE_ON, + HEXAGRAM_STATUS_PARKING_BRAKE_FAULT +} hexagram_status_parking_brake; + +typedef enum { + HEXAGRAM_STATUS_WIPER_WASHER_OK, + HEXAGRAM_STATUS_WIPER_WASHER_LOW, + HEXAGRAM_STATUS_WIPER_WASHER_FAULT +} hexagram_status_wiper_washer; + +typedef enum { + HEXAGRAM_STATUS_LANE_ASSIST_ON, + HEXAGRAM_STATUS_LANE_ASSIST_OFF, + HEXAGRAM_STATUS_LANE_ASSIST_FAULT +} hexagram_status_lane_assist; + +typedef enum { + HEXAGRAM_STATUS_STABILITY_CONTROL_ON, + HEXAGRAM_STATUS_STABILITY_CONTROL_OFF, + HEXAGRAM_STATUS_STABILITY_CONTROL_FAULT +} hexagram_status_stability_control; + +typedef enum { + HEXAGRAM_STATUS_TRACTION_CONTROL_ON, + HEXAGRAM_STATUS_TRACTION_CONTROL_OFF, + HEXAGRAM_STATUS_TRACTION_CONTROL_FAULT +} hexagram_status_traction_control; + +typedef enum { + HEXAGRAM_STATUS_TPMS_OK, + HEXAGRAM_STATUS_TPMS_WARNING, + HEXAGRAM_STATUS_TPMS_FAULT +} hexagram_status_tpms; + +typedef enum { + HEXAGRAM_STATUS_AIRBAG_ON, + HEXAGRAM_STATUS_AIRBAG_OFF, + HEXAGRAM_STATUS_AIRBAG_DEPLOYED, + HEXAGRAM_STATUS_AIRBAG_FAULT +} hexagram_status_airbag; + +typedef enum { + HEXAGRAM_STATUS_BATTERY_OK, + HEXAGRAM_STATUS_BATTERY_FAULT +} hexagram_status_battery; + +typedef enum { + HEXAGRAM_STATUS_ENGINE_OK, + HEXAGRAM_STATUS_ENGINE_FAULT +} hexagram_status_engine; + +typedef enum { + HEXAGRAM_STATUS_FOG_BEAM_OFF, + HEXAGRAM_STATUS_FOG_BEAM_ON, + HEXAGRAM_STATUS_FOG_BEAM_FAULT +} hexagram_status_fog_beam; + +typedef enum { + HEXAGRAM_STATUS_PARKING_BEAM_OFF, + HEXAGRAM_STATUS_PARKING_BEAM_ON, + HEXAGRAM_STATUS_PARKING_BEAM_FAULT +} hexagram_status_parking_beam; + +typedef enum { + HEXAGRAM_STATUS_LOW_BEAM_OFF, + HEXAGRAM_STATUS_LOW_BEAM_ON, + HEXAGRAM_STATUS_LOW_BEAM_FAULT +} hexagram_status_low_beam; + +typedef enum { + HEXAGRAM_STATUS_HIGH_BEAM_OFF, + HEXAGRAM_STATUS_HIGH_BEAM_ON, + HEXAGRAM_STATUS_HIGH_BEAM_FAULT +} hexagram_status_high_beam; + +typedef struct _hexagram_status_icon_box { + hexagram_gauge gauge; + double width, height; + + hexagram_icon *icons; + int state[HEXAGRAM_STATUS_COUNT]; + cairo_surface_t *surface; + int dirty; +} hexagram_status_icon_box; + +int hexagram_status_icon_box_init(hexagram_status_icon_box *box, + double x, + double y, + double width, + double height); + +void hexagram_status_icon_box_cleanup(hexagram_status_icon_box *box); + +void hexagram_status_icon_set(hexagram_status_icon_box *box, + hexagram_status_type type, + int status); + +#endif /* _HEXAGRAM_STATUS_H */ diff --git a/include/hexagram/svg.h b/include/hexagram/svg.h new file mode 100644 index 0000000..2cacbb9 --- /dev/null +++ b/include/hexagram/svg.h @@ -0,0 +1,17 @@ +#ifndef _HEXAGRAM_SVG_H +#define _HEXAGRAM_SVG_H + +#include + +int hexagram_svg_render_to_surface(const char *path, + cairo_surface_t *surface, + double width, + double height, + const char *style); + +cairo_surface_t *hexagram_svg_render_to_image(const char *path, + double width, + double height, + const char *style); + +#endif /* _HEXAGRAM_SVG_H */ diff --git a/include/hexagram/tacho.h b/include/hexagram/tacho.h index 52455f3..3a2b2cb 100644 --- a/include/hexagram/tacho.h +++ b/include/hexagram/tacho.h @@ -1,26 +1,38 @@ #ifndef _HEXAGRAM_TACHO_H #define _HEXAGRAM_TACHO_H -#include +#include +#include +#include #include typedef struct _hexagram_tacho { - hexagram_gauge gauge; + hexagram_dial dial; double redline; + hexagram_trans_gear gear; + hexagram_trans_shift_mode shift_mode; } hexagram_tacho; -void hexagram_tacho_init(hexagram_tacho *tacho, - double x, - double y, - double radius, - double redline); +typedef struct _hexagram_tacho_shift_indicator { + hexagram_gauge gauge; -void hexagram_tacho_draw_face(hexagram_tacho *tacho, - cairo_t *cr); + double rpm_min, + rpm_shift, + rpm; +} hexagram_tacho_shift_indicator; -void hexagram_tacho_draw_needle(hexagram_tacho *tacho, - cairo_t *cr, - double rpm); +int hexagram_tacho_init(hexagram_tacho *tacho, + double x, + double y, + double radius, + double redline, + double rpm_max); + +int hexagram_tacho_shift_indicator_init(hexagram_tacho_shift_indicator *shift, + double x, + double y, + double rpm_min, + double rpm_shift); #endif /* _HEXAGRAM_TACHO_H */ diff --git a/include/hexagram/temp.h b/include/hexagram/temp.h new file mode 100644 index 0000000..1f15bf7 --- /dev/null +++ b/include/hexagram/temp.h @@ -0,0 +1,27 @@ +#ifndef _HEXAGRAM_TEMP_H +#define _HEXAGRAM_TEMP_H + +#include +#include + +typedef enum { + HEXAGRAM_TEMP_CELSIUS, + HEXAGRAM_TEMP_FAHRENHEIT +} hexagram_temp_units; + +typedef struct _hexagram_temp { + hexagram_text text; + hexagram_icon icon; + hexagram_temp_units units; + double value; +} hexagram_temp; + +int hexagram_temp_init(hexagram_temp *temp, + double x, + double y, + hexagram_text_align align, + hexagram_temp_units units); + +void hexagram_temp_cleanup(hexagram_temp *temp); + +#endif /* _HEXAGRAM_TEMP_H */ diff --git a/include/hexagram/text.h b/include/hexagram/text.h new file mode 100644 index 0000000..37412d6 --- /dev/null +++ b/include/hexagram/text.h @@ -0,0 +1,37 @@ +#ifndef _HEXAGRAM_TEXT_H +#define _HEXAGRAM_TEXT_H + +#include +#include + +#include + +#define HEXAGRAM_TEXT_FONT_FACE "Muli" +#define HEXAGRAM_TEXT_FONT_SLANT CAIRO_FONT_SLANT_NORMAL +#define HEXAGRAM_TEXT_FONT_WEIGHT CAIRO_FONT_WEIGHT_BOLD +#define HEXAGRAM_TEXT_FONT_SIZE 24 + +typedef enum { + HEXAGRAM_TEXT_LEFT, + HEXAGRAM_TEXT_MIDDLE, + HEXAGRAM_TEXT_RIGHT +} hexagram_text_align; + +typedef struct _hexagram_text hexagram_text; + +typedef ssize_t (hexagram_text_format_callback)(hexagram_text *, char *, size_t); + +typedef struct _hexagram_text { + hexagram_gauge gauge; + hexagram_text_align align; + hexagram_text_format_callback *format_text; + char buf[128]; +} hexagram_text; + +int hexagram_text_init(hexagram_text *text, + double x, + double y, + hexagram_text_align align, + hexagram_text_format_callback *format_text); + +#endif /* _HEXAGRAM_TEXT_H */ diff --git a/include/hexagram/thermo.h b/include/hexagram/thermo.h index 85d87cf..75d6c43 100644 --- a/include/hexagram/thermo.h +++ b/include/hexagram/thermo.h @@ -3,24 +3,18 @@ #include -#include +#include +#include typedef struct _hexagram_thermo { - hexagram_gauge gauge; - double redline; + hexagram_dial dial; + hexagram_temp_units units; + double level_warn; } hexagram_thermo; -void hexagram_thermo_init(hexagram_thermo *thermo, - double x, - double y, - double radius, - double redline); - -void hexagram_thermo_draw_face(hexagram_thermo *thermo, - cairo_t *cr); - -void hexagram_thermo_draw_needle(hexagram_thermo *thermo, - cairo_t *cr, - double temp); +int hexagram_thermo_init(hexagram_thermo *thermo, + double x, + double y, + double radius); #endif /* _HEXAGRAM_THERMO_H */ diff --git a/include/hexagram/trans.h b/include/hexagram/trans.h new file mode 100644 index 0000000..3e6420b --- /dev/null +++ b/include/hexagram/trans.h @@ -0,0 +1,35 @@ +#ifndef _HEXAGRAM_TRANS_H +#define _HEXAGRAM_TRANS_H + +typedef enum { + HEXAGRAM_TRANS_GEAR_S = -4, + HEXAGRAM_TRANS_GEAR_D = -3, + HEXAGRAM_TRANS_GEAR_P = -2, + HEXAGRAM_TRANS_GEAR_R = -1, + HEXAGRAM_TRANS_GEAR_N = 0, + HEXAGRAM_TRANS_GEAR_1 = 1, + HEXAGRAM_TRANS_GEAR_2 = 2, + HEXAGRAM_TRANS_GEAR_3 = 3, + HEXAGRAM_TRANS_GEAR_4 = 4, + HEXAGRAM_TRANS_GEAR_5 = 5, + HEXAGRAM_TRANS_GEAR_6 = 6, + HEXAGRAM_TRANS_GEAR_7 = 7, + HEXAGRAM_TRANS_GEAR_8 = 8, + HEXAGRAM_TRANS_GEAR_9 = 9, + HEXAGRAM_TRANS_GEAR_10 = 10 +} hexagram_trans_gear; + +typedef enum { + HEXAGRAM_TRANS_SHIFT_NORMAL, + HEXAGRAM_TRANS_SHIFT_COMFORT, + HEXAGRAM_TRANS_SHIFT_ECO, + HEXAGRAM_TRANS_SHIFT_SPORT, + HEXAGRAM_TRANS_SHIFT_RACE, + HEXAGRAM_TRANS_SHIFT_MANUAL, +} hexagram_trans_shift_mode; + +char *hexagram_trans_gear_str(hexagram_trans_gear gear); + +char *hexagram_trans_shift_mode_str(hexagram_trans_shift_mode mode); + +#endif /* _HEXAGRAM_TRANS_H */ diff --git a/src/Makefile b/src/Makefile index f527c6c..f365c88 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,18 +4,20 @@ INCLUDE_PATH = ../include HEADER_SUBDIR = hexagram CC = $(CROSS)cc -CFLAGS += -fPIC -I$(INCLUDE_PATH) $(shell pkg-config --cflags cairo x11) -LDFLAGS = $(shell pkg-config --libs cairo x11) -lXext +CFLAGS += -fPIC -I$(INCLUDE_PATH) $(shell pkg-config --cflags cairo x11 librsvg-2.0) +LDFLAGS = $(shell pkg-config --libs cairo x11 librsvg-2.0) -lXext HEADERS = list.h dict.h hash.h can.h capture.h pcapng.h module.h \ - window.h gauge.h tacho.h speedo.h thermo.h fuel.h mfd.h \ - cluster.h sim.h schedule.h + window.h path.h pattern.h gauge.h dial.h text.h odo.h clock.h \ + temp.h trans.h tacho.h speedo.h thermo.h fuel.h mfd.h \ + cluster.h sim.h schedule.h svg.h icon.h status.h HEADERS_LOCAL = util.h OBJS = list.o dict.o hash.o can.o capture.o pcapng.o module.o \ - window.o gauge.o tacho.o speedo.o thermo.o fuel.o mfd.o \ - cluster.o sim.o schedule.o + window.o path.o pattern.o gauge.o dial.o text.o odo.o clock.o \ + temp.o trans.o tacho.o speedo.o thermo.o fuel.o mfd.o \ + cluster.o sim.o schedule.o svg.o icon.o status.o VERSION_MAJOR = 0 VERSION_MINOR = 0.1 diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 0000000..bda2e33 --- /dev/null +++ b/src/clock.c @@ -0,0 +1,54 @@ +#include +#include + +#include + +static ssize_t format_text(hexagram_text *text, char *buf, size_t len) { + hexagram_clock *clock = (hexagram_clock *)text; + switch (clock->format) { + case HEXAGRAM_CLOCK_24H: + return snprintf(buf, len, "%d:%02d", + clock->time.hour, clock->time.minute); + + case HEXAGRAM_CLOCK_12H: + return snprintf(buf, len, "%d:%02d %s", + clock->time.hour, clock->time.minute, + clock->time.hour < 12? "am": "pm"); + } + + return 0; +} + +int hexagram_clock_init(hexagram_clock *clock, + double x, + double y, + hexagram_text_align align, + hexagram_clock_format format) { + if (hexagram_text_init(&clock->text, + x, + y, + align, + format_text) < 0) { + goto error_text_init; + } + + clock->format = format; + clock->time.hour = 0; + clock->time.minute = 0; + + return 0; + +error_text_init: + return -1; +} + +void hexagram_clock_set(hexagram_clock *clock, time_t now) { + struct tm *now_tm; + + if ((now_tm = localtime(&now)) == NULL) { + return; + } + + clock->time.hour = now_tm->tm_hour; + clock->time.minute = now_tm->tm_min; +} diff --git a/src/cluster.c b/src/cluster.c index b5492e2..d74bcf6 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -3,74 +3,68 @@ #include -void hexagram_cluster_resize(hexagram_cluster *cluster, - double width, - double height) { - /* - * Ensure correct cluster aspect ratio - */ - if (width / height > 32.0 / 15.0) { - width = height * (32.0 / 15.0); - } else { - height = width * (15.0 / 32.0); - } +#define CLUSTER_WIDTH 1280 +#define CLUSTER_HEIGHT 480 - hexagram_tacho_init(&cluster->tacho, - width * 0.2, - height * 0.5, - height * 0.4, - 6500.0); +#define SHIFT_INDICATOR_X 392 +#define SHIFT_INDICATOR_Y 24 - hexagram_speedo_init(&cluster->speedo, - width * 0.8, - height * 0.5, - height * 0.4); +#define SECTION_HEIGHT_TOP 64 +#define SECTION_HEIGHT_BOTTOM 52 - hexagram_thermo_init(&cluster->thermo, - width * 0.43, - height * 0.76, - height * 0.13, - 230.0); +#define SPEEDO_MAX 200.0 +#define SPEEDO_UNITS HEXAGRAM_SPEEDO_MPH - hexagram_fuel_init(&cluster->fuel, - width * 0.57, - height * 0.76, - height * 0.13, - 0.14); +#define PATTERN_WIDTH 100 +#define PATTERN_HEIGHT 87 - hexagram_mfd_init(&cluster->mfd, - width * 0.42, - height * 0.10, - width * 0.16, - height * 0.50); +static const double hexagon_pattern_color[3] = { 0.3, 0.1, 0.3 }; - cluster->width = width; - cluster->height = height; -} +static const double hexagon_pattern_matrix[6] = { + 0.21213203, 0.21213203, 0.21213203, -0.21213203, 1, 1 +}; -void hexagram_cluster_init(hexagram_cluster *cluster, - double width, - double height) { - hexagram_cluster_resize(cluster, width, height); +static const double hexagon_pattern_scale[2] = { 3, 3 }; - memset(&cluster->state, '\0', sizeof(cluster->state)); -} - -hexagram_cluster *hexagram_cluster_new(double width, - double height) { - hexagram_cluster *cluster; - - if ((cluster = malloc(sizeof(*cluster))) == NULL) { - goto error_malloc_cluster; - } - - hexagram_cluster_init(cluster, width, height); - - return cluster; - -error_malloc_cluster: - return NULL; -} +static const char *hexagon_pattern = + "M 23.246093 0," + " 0 13.382812," + "V 44.689453" + "L 0.0839844 44.640625," + " 24.033203 58.505859," + " 24.001953 86.332031," + " 22.841796 87" + "h 4.478516" + "L 26.068359 86.275391," + " 26.099609 58.449219," + " 50.083984 44.640625," + " 74.033203 58.505859," + " 74.001953 86.332031," + " 72.841796 87" + "h 4.478516" + "L 76.068359 86.275391," + " 76.099609 58.449219," + " 100 44.689453," + "V 13.365234" + "L 76.919921 0" + "H 73.246093" + "L 50.015625 13.373047," + " 26.919921 0" + "Z" + "M 25.083984 1.25," + " 49.033203 15.115234," + " 49.001953, 42.941406," + " 25.017578 56.75," + " 1.0019531 42.845703," + "l 0.033203 -27.75" + "z" + "m 50 0," + " 24.017576 13.904297," + " -0.0352 27.75" + "L 75.017578 56.75," + " 51.068359 42.884766," + " 51.099609 15.058594" + "Z"; int hexagram_cluster_filter_can_if(hexagram_can_if *can_if) { struct can_filter filters[4] = { @@ -87,65 +81,202 @@ int hexagram_cluster_filter_can_if(hexagram_can_if *can_if) { sizeof(filters)); } -int hexagram_cluster_update(hexagram_cluster *cluster, - struct can_frame *frame) { - switch (frame->can_id) { - case 0x280: { - cluster->state.rpm = 0.25 * - (double)(frame->data[2] | (frame->data[3] << 8)); +int hexagram_cluster_init(hexagram_cluster *cluster, + double rpm_min, + double rpm_redline, + double rpm_max) { + cluster->width = CLUSTER_WIDTH; + cluster->height = CLUSTER_HEIGHT; - return 1; - } - - case 0x288: { - cluster->state.temp = 0.75 * (double)(frame->data[1] - 48); - - return 1; - } - - case 0x320: { - cluster->state.fuel = (double)(frame->data[2] & 0xf) / 15.0; - - return 1; - } - - case 0x5a0: { - cluster->state.rps = 0.001 * (double)(frame->data[1] - | (frame->data[2] << 8)); - - return 1; - } + if (hexagram_pattern_init(&cluster->pattern, + PATTERN_WIDTH, + PATTERN_HEIGHT, + hexagon_pattern_color, + HEXAGRAM_PATTERN_STROKE, + hexagon_pattern, + strlen(hexagon_pattern)) < 0) { + goto error_pattern_init; } + if (hexagram_speedo_init(&cluster->speedo, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + SPEEDO_MAX, + SPEEDO_UNITS) < 0) { + goto error_speedo_init; + } + + if (hexagram_fuel_init(&cluster->fuel, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2) < 0) { + goto error_fuel_init; + } + + if (hexagram_tacho_init(&cluster->tacho, + CLUSTER_WIDTH - CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + rpm_redline, + rpm_max) < 0) { + goto error_tacho_init; + } + + if (hexagram_thermo_init(&cluster->thermo, + CLUSTER_WIDTH - CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2, + CLUSTER_HEIGHT / 2) < 0) { + goto error_thermo_init; + } + + if (hexagram_odo_init(&cluster->odo, + 0.8 * CLUSTER_HEIGHT, + CLUSTER_HEIGHT - 16, + HEXAGRAM_TEXT_LEFT, + HEXAGRAM_ODO_MILES) < 0) { + goto error_odo_init; + } + + if (hexagram_clock_init(&cluster->clock, + CLUSTER_WIDTH / 2, + CLUSTER_HEIGHT - 16, + HEXAGRAM_TEXT_MIDDLE, + HEXAGRAM_CLOCK_24H) < 0) { + goto error_clock_init; + } + + if (hexagram_temp_init(&cluster->temp, + CLUSTER_WIDTH - 0.8 * CLUSTER_HEIGHT, + CLUSTER_HEIGHT - 16, + HEXAGRAM_TEXT_RIGHT, + HEXAGRAM_TEMP_CELSIUS) < 0) { + goto error_temp_init; + } + + if (hexagram_tacho_shift_indicator_init(&cluster->shift_indicator, + SHIFT_INDICATOR_X, + SHIFT_INDICATOR_Y, + rpm_min, + rpm_redline) < 0) { + goto error_tacho_shift_indicator_init; + } + + if (hexagram_status_icon_box_init(&cluster->status, + CLUSTER_WIDTH / 2 - HEXAGRAM_STATUS_ICON_WIDTH * 3.5, + CLUSTER_HEIGHT - SECTION_HEIGHT_BOTTOM - 16 - HEXAGRAM_STATUS_ICON_HEIGHT * 3, + HEXAGRAM_STATUS_ICON_WIDTH * 7, + HEXAGRAM_STATUS_ICON_HEIGHT * 3) < 0) { + goto error_status_icon_box_init; + } + + cluster->gauges[0] = &cluster->speedo.dial.gauge; + cluster->gauges[1] = &cluster->fuel.dial.gauge; + cluster->gauges[2] = &cluster->tacho.dial.gauge; + cluster->gauges[3] = &cluster->thermo.dial.gauge; + cluster->gauges[4] = &cluster->odo.text.gauge; + cluster->gauges[5] = &cluster->temp.text.gauge; + cluster->gauges[6] = &cluster->clock.text.gauge; + cluster->gauges[7] = &cluster->status.gauge; + cluster->gauges[8] = &cluster->shift_indicator.gauge; + return 0; + +error_status_icon_box_init: +error_tacho_shift_indicator_init: + hexagram_temp_cleanup(&cluster->temp); + +error_temp_init: +error_clock_init: +error_odo_init: +error_thermo_init: +error_tacho_init: +error_fuel_init: +error_speedo_init: + hexagram_pattern_cleanup(&cluster->pattern); + +error_pattern_init: + return -1; } -void hexagram_cluster_draw_bg(hexagram_cluster *cluster, - cairo_t *cr) { - /* - * Paint canvas black - */ - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_paint(cr); - - hexagram_tacho_draw_face(&cluster->tacho, cr); - hexagram_speedo_draw_face(&cluster->speedo, cr); - hexagram_thermo_draw_face(&cluster->thermo, cr); - hexagram_fuel_draw_face(&cluster->fuel, cr); - hexagram_mfd_draw(&cluster->mfd, cr); +void hexagram_cluster_cleanup(hexagram_cluster *cluster) { + hexagram_status_icon_box_cleanup(&cluster->status); + hexagram_temp_cleanup(&cluster->temp); + hexagram_pattern_cleanup(&cluster->pattern); } -void hexagram_cluster_draw_fg(hexagram_cluster *cluster, - cairo_t *cr) { - hexagram_tacho_draw_needle(&cluster->tacho, cr, - cluster->state.rpm); +hexagram_cluster *hexagram_cluster_new(double rpm_min, + double rpm_redline, + double rpm_max) { + hexagram_cluster *cluster; - hexagram_speedo_draw_needle(&cluster->speedo, cr, - cluster->state.rps); + if ((cluster = malloc(sizeof(*cluster))) == NULL) { + goto error_malloc_cluster; + } - hexagram_thermo_draw_needle(&cluster->thermo, cr, - cluster->state.temp); + hexagram_cluster_init(cluster, rpm_min, rpm_redline, rpm_max); - hexagram_fuel_draw_needle(&cluster->fuel, cr, - cluster->state.fuel); + return cluster; + +error_malloc_cluster: + return NULL; +} + +void hexagram_cluster_destroy(hexagram_cluster *cluster) { + hexagram_cluster_cleanup(cluster); + free(cluster); +} + +void hexagram_cluster_draw_bg(hexagram_cluster *cluster, cairo_t *cr) { + int i; + + cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); + cairo_rectangle(cr, 0, 0, CLUSTER_WIDTH, CLUSTER_HEIGHT); + cairo_fill(cr); + + cairo_rectangle(cr, 0, 0, CLUSTER_WIDTH, CLUSTER_HEIGHT); + hexagram_pattern_fill(&cluster->pattern, + cr, + hexagon_pattern_matrix, + hexagon_pattern_scale); + + /* Top dark area */ + cairo_set_source_rgba(cr, 0, 0, 0, 0.5); + cairo_rectangle(cr, 0, 0, CLUSTER_WIDTH, SECTION_HEIGHT_TOP); + cairo_fill(cr); + + cairo_set_source_rgba(cr, 1, 1, 1, 0.25); + cairo_move_to(cr, 0, SECTION_HEIGHT_TOP); + cairo_line_to(cr, CLUSTER_WIDTH, SECTION_HEIGHT_TOP); + cairo_stroke(cr); + + /* Bottom dark area */ + cairo_set_source_rgba(cr, 0, 0, 0, 0.5); + cairo_rectangle(cr, + 0, + CLUSTER_HEIGHT - SECTION_HEIGHT_BOTTOM, + CLUSTER_WIDTH, + SECTION_HEIGHT_BOTTOM); + cairo_fill(cr); + + cairo_set_source_rgba(cr, 1, 1, 1, 0.25); + cairo_move_to(cr, 0, CLUSTER_HEIGHT - SECTION_HEIGHT_BOTTOM); + cairo_line_to(cr, CLUSTER_WIDTH, CLUSTER_HEIGHT - SECTION_HEIGHT_BOTTOM); + cairo_stroke(cr); + + for (i=0; igauges[i]->draw_bg) { + cluster->gauges[i]->draw_bg(cluster->gauges[i], cr); + } + } +} + +void hexagram_cluster_draw_fg(hexagram_cluster *cluster, cairo_t *cr) { + int i; + + for (i=0; igauges[i]->draw_fg) { + cluster->gauges[i]->draw_fg(cluster->gauges[i], cr); + } + } } diff --git a/src/dial.c b/src/dial.c new file mode 100644 index 0000000..312aad9 --- /dev/null +++ b/src/dial.c @@ -0,0 +1,305 @@ +#include +#include + +#include + +#define RAD (M_PI / 180.0) +#define ANGLE_OFFSET (M_PI / 2.0) + +#define DIAL_MAIN_BEZEL_WIDTH 16 + +#define DIAL_MAIN_MARK_RADIUS_MIN +#define DIAL_MAIN_MARK_RADIUS_MAX + +static inline double _min(double a, double b) { + return a < b? + a: b; +} + +static cairo_pattern_t *gradient_create(hexagram_dial *dial) { + const double stops[2][5] = { + { 0, 1.0, 0.4, 1.0, 1.0 }, + { 1, 0.4, 0.0, 0.4, 0.0 } + }; + + cairo_pattern_t *pattern; + + size_t i; + + if ((pattern = cairo_pattern_create_linear(dial->gauge.x - dial->radius, + dial->gauge.y - dial->radius, + dial->gauge.x + dial->radius, + dial->gauge.y + dial->radius)) == NULL) { + goto error_cairo_pattern_create_linear; + } + + for (i=0; i<2; i++) { + cairo_pattern_add_color_stop_rgba(pattern, + stops[i][0], + stops[i][1], + stops[i][2], + stops[i][3], + stops[i][4]); + } + + return pattern; + +error_cairo_pattern_create_linear: + return NULL; +} + +static int main_draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_dial *dial = (hexagram_dial *)gauge; + + double arc[5] = { + dial->gauge.x, + dial->gauge.y, + dial->radius - DIAL_MAIN_BEZEL_WIDTH, + 0, + 2.0 * M_PI + }; + + /* Gauge face */ + cairo_set_source_rgba(cr, 0, 0, 0, 1); + cairo_arc(cr, arc[0], arc[1], arc[2], arc[3], arc[4]); + cairo_fill(cr); + + /* Gauge bezel */ + cairo_set_source(cr, dial->gradient); + cairo_set_line_width(cr, DIAL_MAIN_BEZEL_WIDTH); + cairo_arc(cr, arc[0], arc[1], arc[2], arc[3], arc[4]); + cairo_stroke(cr); + + /* Gauge centre bezel */ + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_set_line_width(cr, DIAL_MAIN_BEZEL_WIDTH / 3.0); + cairo_arc(cr, dial->gauge.x, dial->gauge.y, dial->radius / 2, 0, 2.0 * M_PI); + cairo_stroke(cr); + + return 0; +} + +static int main_draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_dial *dial = (hexagram_dial *)gauge; + + double angle = hexagram_dial_angle(dial, dial->value); + + cairo_set_line_width(cr, dial->radius * 0.375); + cairo_set_source_rgba(cr, 1, 0.4, 1, 0.25); + + cairo_arc(cr, + dial->gauge.x, + dial->gauge.y, + dial->radius * 0.713, + dial->min_angle - ANGLE_OFFSET - RAD * 1.5, + angle); + + cairo_stroke(cr); + + hexagram_dial_draw_needle(dial, cr, 0.5, 0.89, dial->value); + + return 0; +} + +static int bottom_draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_dial *dial = (hexagram_dial *)gauge; + + cairo_set_line_width(cr, 24); + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + + cairo_arc(cr, + dial->gauge.x, + dial->gauge.y, + dial->radius - 36, + dial->min_angle - ANGLE_OFFSET - 4 * RAD, + dial->max_angle - ANGLE_OFFSET + 4 * RAD); + + cairo_stroke(cr); + + return 0; +} + +static int bottom_draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_dial *dial = (hexagram_dial *)gauge; + + double min_radius = 0.8, + max_radius = 0.9; + + return hexagram_dial_draw_needle(dial, cr, min_radius, max_radius, dial->value); +} + +int hexagram_dial_init(hexagram_dial *dial, + hexagram_dial_type type, + double x, + double y, + double radius, + double min_angle, + double max_angle, + double min_value, + double max_value) { + dial->gauge.x = x; + dial->gauge.y = y; + dial->type = type; + dial->radius = radius; + dial->min_angle = min_angle; + dial->max_angle = max_angle; + dial->min_value = min_value; + dial->max_value = max_value; + + dial->needle_min_radius = 0; + dial->needle_max_radius = 0; + + dial->mark_min_radius = 0; + dial->mark_max_radius = 0; + + dial->value = min_value; + + switch (type) { + case HEXAGRAM_DIAL_MAIN: + if ((dial->gradient = gradient_create(dial)) == NULL) { + goto error_gradient_create; + } + + dial->gauge.draw_bg = main_draw_bg; + dial->gauge.draw_fg = main_draw_fg; + + break; + + case HEXAGRAM_DIAL_BOTTOM: + dial->gradient = NULL; + + dial->gauge.draw_bg = bottom_draw_bg; + dial->gauge.draw_fg = bottom_draw_fg; + + break; + } + + return 0; + +error_gradient_create: + return -1; +} + +double hexagram_dial_angle(hexagram_dial *dial, double value) { + double adj_max = dial->max_value - dial->min_value, + adj = _min(adj_max, value - dial->min_value); + + double angle = dial->min_angle + + (dial->max_angle - dial->min_angle) * (adj / adj_max) - ANGLE_OFFSET; + + switch (dial->type) { + case HEXAGRAM_DIAL_MAIN: + return angle; + + case HEXAGRAM_DIAL_BOTTOM: + return (dial->max_angle - angle) + dial->min_angle + M_PI; + } + + return 0; +} + +int hexagram_dial_draw_legend(hexagram_dial *dial, + cairo_t *cr, + double radius, + double value, + char *text) { + double angle = hexagram_dial_angle(dial, value); + cairo_text_extents_t extents; + + cairo_text_extents(cr, text, &extents); + + cairo_move_to(cr, + dial->gauge.x + radius * dial->radius * cos(angle) - extents.width / 1.5, + dial->gauge.y + radius * dial->radius * sin(angle) + extents.height / 4); + + cairo_show_text(cr, text); + cairo_stroke(cr); + + return 0; +} + +int hexagram_dial_draw_legend_offset(hexagram_dial *dial, + cairo_t *cr, + double radius, + double value, + char *text, + double x_offset, + double y_offset) { + double angle = hexagram_dial_angle(dial, value); + + cairo_move_to(cr, + dial->gauge.x + radius * dial->radius * cos(angle) + x_offset, + dial->gauge.y + radius * dial->radius * sin(angle) + y_offset); + + cairo_show_text(cr, text); + cairo_stroke(cr); + + return 0; +} + +int hexagram_dial_draw_mark(hexagram_dial *dial, + cairo_t *cr, + double min_radius, + double max_radius, + double value) { + double angle = hexagram_dial_angle(dial, value); + + cairo_move_to(cr, + dial->gauge.x + min_radius * dial->radius * cos(angle), + dial->gauge.y + min_radius * dial->radius * sin(angle)); + + cairo_line_to(cr, + dial->gauge.x + max_radius * dial->radius * cos(angle), + dial->gauge.y + max_radius * dial->radius * sin(angle)); + + cairo_stroke(cr); + + return 0; +} + +int hexagram_dial_draw_needle(hexagram_dial *dial, + cairo_t *cr, + double min_radius, + double max_radius, + double value) { + double angle = hexagram_dial_angle(dial, value); + + cairo_set_source_rgba(cr, 1, 0.4, 1, 0.75); + cairo_set_line_width(cr, 8); + + cairo_move_to(cr, + dial->gauge.x + min_radius * dial->radius * cos(angle), + dial->gauge.y + min_radius * dial->radius * sin(angle)); + + cairo_line_to(cr, + dial->gauge.x + (max_radius * dial->radius) * cos(angle), + dial->gauge.y + (max_radius * dial->radius) * sin(angle)); + + cairo_stroke(cr); + + return 0; +} + +int hexagram_dial_draw_bg(hexagram_dial *dial, cairo_t *cr) { + switch (dial->type) { + case HEXAGRAM_DIAL_MAIN: + return main_draw_bg(&dial->gauge, cr); + + case HEXAGRAM_DIAL_BOTTOM: + return bottom_draw_bg(&dial->gauge, cr); + } + + return 0; +} + +int hexagram_dial_draw_fg(hexagram_dial *dial, cairo_t *cr) { + switch (dial->type) { + case HEXAGRAM_DIAL_MAIN: + return main_draw_fg(&dial->gauge, cr); + + case HEXAGRAM_DIAL_BOTTOM: + return bottom_draw_fg(&dial->gauge, cr); + } + + return 0; +} diff --git a/src/fuel.c b/src/fuel.c index 412f7e0..6441a1e 100644 --- a/src/fuel.c +++ b/src/fuel.c @@ -1,88 +1,87 @@ +#include #include #include #include -void hexagram_fuel_init(hexagram_fuel *fuel, - double x, - double y, - double radius, - double redline) { - hexagram_gauge_init(&fuel->gauge, x, y, radius, - 300.0 * (M_PI / 180.0), - 420.0 * (M_PI / 180.0)); +#define RAD (M_PI / 180.0) - fuel->redline = redline; -} +#define LEVEL_MIN 0 +#define LEVEL_WARN 2 +#define LEVEL_MAX 15 -void hexagram_fuel_draw_face(hexagram_fuel *fuel, - cairo_t *cr) { - int i; +#define ANGLE_MIN (144.0 * RAD) +#define ANGLE_MAX (216.0 * RAD) + +#define FONT_FACE "Muli" + +static int draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_fuel *fuel = (hexagram_fuel *)gauge; + int level; + + if (hexagram_dial_draw_bg(&fuel->dial, cr) < 0) { + goto error_dial_draw_bg; + } + + cairo_select_font_face(cr, + FONT_FACE, + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size(cr, fuel->dial.radius * 0.1); + + cairo_set_source_rgb(cr, 1, 0, 0); + hexagram_dial_draw_legend_offset(&fuel->dial, cr, 0.75, 0, "0", -5, 5); cairo_set_source_rgb(cr, 1, 1, 1); + hexagram_dial_draw_legend_offset(&fuel->dial, cr, 0.75, fuel->dial.max_value, "1", -10, 5); - cairo_arc(cr, - fuel->gauge.x, - fuel->gauge.y, - fuel->gauge.radius, - 0, - 2.0 * M_PI); + cairo_set_line_width(cr, 6.0); - cairo_stroke(cr); + for (level=0; level<=LEVEL_MAX; level++) { + double min_radius; - cairo_move_to(cr, - fuel->gauge.x - 0.8 * fuel->gauge.radius, - fuel->gauge.y - 0.1 * fuel->gauge.radius); - - cairo_show_text(cr, "0"); - - cairo_move_to(cr, - fuel->gauge.x - 0.15 * fuel->gauge.radius, - fuel->gauge.y - 0.50 * fuel->gauge.radius); - - cairo_show_text(cr, "1/2"); - - cairo_move_to(cr, - fuel->gauge.x + 0.65 * fuel->gauge.radius, - fuel->gauge.y - 0.10 * fuel->gauge.radius); - - cairo_show_text(cr, "1"); - - /* - * Draw gauge graduations - */ - for (i=0; i<=16; i++) { - double min_radius = (i <= 4)? - (i % 2? 0.8: 0.7): - (i % 4? 0.8: 0.7); - - if (i <= (16.0 * fuel->redline)) { + if (level < 2) { + min_radius = 0.84; + cairo_set_source_rgb(cr, 1, 0, 0); + } else if (level < 4) { + min_radius = 0.82; cairo_set_source_rgb(cr, 1, 0, 0); } else { + min_radius = 0.82; cairo_set_source_rgb(cr, 1, 1, 1); } - hexagram_gauge_draw_mark(&fuel->gauge, - cr, - min_radius, - 0.9, - (double)i / 16.0); - } -} - -void hexagram_fuel_draw_needle(hexagram_fuel *fuel, - cairo_t *cr, - double level) { - if (level < 0.0) { - level = 0.0; - } else if (level > 1.0) { - level = 1.0; + hexagram_dial_draw_mark(&fuel->dial, cr, min_radius, 0.87, level); } - cairo_stroke(cr); + return 0; - hexagram_gauge_draw_needle(&fuel->gauge, - cr, - 0.9, - level); +error_dial_draw_bg: + return -1; +} + +int hexagram_fuel_init(hexagram_fuel *fuel, + double x, + double y, + double radius) { + if (hexagram_dial_init(&fuel->dial, + HEXAGRAM_DIAL_BOTTOM, + x, + y, + radius, + ANGLE_MIN, + ANGLE_MAX, + LEVEL_MIN, + LEVEL_MAX) < 0) { + goto error_dial_init; + } + + fuel->dial.gauge.draw_bg = draw_bg; + fuel->dial.value = LEVEL_MIN; + + return 0; + +error_dial_init: + return -1; } diff --git a/src/gauge.c b/src/gauge.c index 7d8924d..eb88a31 100644 --- a/src/gauge.c +++ b/src/gauge.c @@ -6,87 +6,10 @@ void hexagram_gauge_init(hexagram_gauge *gauge, double x, double y, - double radius, - double min_angle, - double max_angle) { - gauge->x = x; - gauge->y = y; - gauge->radius = radius; - gauge->min_angle = min_angle; - gauge->max_angle = max_angle; -} - -void hexagram_gauge_draw_number(hexagram_gauge *gauge, - cairo_t *cr, - double radius, - double value, - const char *text) { - double angle = gauge->min_angle - + ((gauge->max_angle - gauge->min_angle) * value) - 1.658; - - cairo_set_source_rgb(cr, 1, 1, 1); - - cairo_move_to(cr, - gauge->x + (radius * gauge->radius) * cos(angle), - gauge->y + (radius * gauge->radius) * sin(angle)); - - cairo_save(cr); - cairo_rotate(cr, angle + 1.658); - cairo_show_text(cr, text); - cairo_stroke(cr); - cairo_restore(cr); -} - -void hexagram_gauge_draw_mark(hexagram_gauge *gauge, - cairo_t *cr, - double min_radius, - double max_radius, - double value) { - double angle = gauge->min_angle - + ((gauge->max_angle - gauge->min_angle) * value) - (M_PI / 2.0); - - cairo_move_to(cr, - gauge->x + (min_radius * gauge->radius) * cos(angle), - gauge->y + (min_radius * gauge->radius) * sin(angle)); - - cairo_line_to(cr, - gauge->x + (max_radius * gauge->radius) * cos(angle), - gauge->y + (max_radius * gauge->radius) * sin(angle)); - - cairo_stroke(cr); -} - -void hexagram_gauge_draw_needle(hexagram_gauge *gauge, - cairo_t *cr, - double radius, - double value) { - double angle = gauge->min_angle - + ((gauge->max_angle - gauge->min_angle) * value) - (M_PI / 2.0); - - /* - * Draw the gauge pivot - */ - cairo_set_source_rgb(cr, 0.4, 0.4, 0.4); - - cairo_arc(cr, - gauge->x, - gauge->y, - gauge->radius * 0.08, - 0, - 2.0 * M_PI); - - cairo_stroke(cr); - - /* - * Draw the needle above the pivot - */ - cairo_move_to(cr, gauge->x, gauge->y); - - cairo_set_source_rgb(cr, 0, 0.25, 1.0); - - cairo_line_to(cr, - gauge->x + (radius * gauge->radius) * cos(angle), - gauge->y + (radius * gauge->radius) * sin(angle)); - - cairo_stroke(cr); + hexagram_gauge_draw_callback *draw_bg, + hexagram_gauge_draw_callback *draw_fg) { + gauge->x = x; + gauge->y = y; + gauge->draw_bg = draw_bg; + gauge->draw_fg = draw_fg; } diff --git a/src/icon.c b/src/icon.c new file mode 100644 index 0000000..3150042 --- /dev/null +++ b/src/icon.c @@ -0,0 +1,151 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ICON_DIR_COUNT 3 + +static const char *icon_dirs[ICON_DIR_COUNT] = { + "./icons", + "/usr/local/share/hexagram/icons", + "/usr/share/hexagram/icons" +}; + +static const char *icon_colors[HEXAGRAM_ICON_COLOR_COUNT] = { + "#f00", "#fa0", "#ff0", "#0f0", "#00f", "#fff" +}; + +static cairo_surface_t *load_to_surface(const char *path, + const char *style_format, + const char *color, + double width, + double height) { + cairo_surface_t *surface; + char *style; + + if (asprintf(&style, style_format, color) < 0) { + goto error_asprintf_style; + } + + if ((surface = hexagram_svg_render_to_image(path, + width, + height, + style)) == NULL) { + goto error_svg_render_to_image; + } + + free(style); + + return surface; + +error_svg_render_to_image: + free(style); + +error_asprintf_style: + return NULL; +} + +int hexagram_icon_init(hexagram_icon *icon, double width, double height) { + char path[128]; + ssize_t i, c; + + icon->width = width; + icon->height = height; + + for (c=0; csurfaces[c] = NULL; + } + + for (i=0; iname) + 5; + struct stat st; + + if (len > sizeof(path)) { + errno = ENAMETOOLONG; + + goto error_nametoolong; + } + + if (snprintf(path, len, "%s/%s.svg", icon_dirs[i], icon->name) < 0) { + goto error_snprintf; + } + + if (stat(path, &st) < 0) { + continue; + } + + for (c=0; ccolors[c]) { + continue; + } + + if ((icon->surfaces[c] = load_to_surface(path, + icon->style_format, + icon_colors[c], + width, + height)) == NULL) { + goto error_load_to_surface; + } + } + } + + return 0; + +error_load_to_surface: +error_snprintf: +error_nametoolong: + while (--c >= 0) { + cairo_surface_destroy(icon->surfaces[c]); + } + + return -1; +} + +void hexagram_icon_cleanup(hexagram_icon *icon) { + size_t c; + + for (c=0; csurfaces[c]) { + cairo_surface_destroy(icon->surfaces[c]); + } + } +} + +int hexagram_icon_drawable(hexagram_icon *icon, int status) { + int color; + + for (color=0; colorcolors[color] == status && icon->surfaces[color] != NULL) { + return 1; + } + } + + return 0; +} + +int hexagram_icon_draw(hexagram_icon *icon, + cairo_t *cr, + double x, + double y, + int status) { + int color; + + for (color=0; colorcolors[color] == status && icon->surfaces[color] != NULL) { + cairo_set_source_surface(cr, icon->surfaces[color], x, y); + cairo_rectangle(cr, x, y, icon->width, icon->height); + cairo_fill(cr); + + goto done; + } + } + +done: + return 0; +} diff --git a/src/odo.c b/src/odo.c new file mode 100644 index 0000000..15995b1 --- /dev/null +++ b/src/odo.c @@ -0,0 +1,31 @@ +#include + +#include + +static ssize_t format_text(hexagram_text *text, char *buf, size_t len) { + hexagram_odo *odo = (hexagram_odo *)text; + + return snprintf(buf, len, "%.1f mi", odo->value); +} + +int hexagram_odo_init(hexagram_odo *odo, + double x, + double y, + hexagram_text_align align, + hexagram_odo_units units) { + if (hexagram_text_init(&odo->text, + x, + y, + align, + format_text) < 0) { + goto error_text_init; + } + + odo->units = units; + odo->value = 0; + + return 0; + +error_text_init: + return -1; +} diff --git a/src/path.c b/src/path.c new file mode 100644 index 0000000..851fe9d --- /dev/null +++ b/src/path.c @@ -0,0 +1,286 @@ +#include +#include +#include + +#include + +static inline int is_alpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static inline int is_num(char c) { + return c >= '0' && c <= '9'; +} + +static inline int is_num_start(char c) { + return is_num(c) || c == '-'; +} + +static inline int is_sep(char c) { + return c == ',' || c == ' ' || c == '\t' || c == '\n'; +} + +struct arg { + ssize_t start, end; + hexagram_path_arg_type type; + char buf[32]; +}; + +struct command { + char c; + int stride, first, argc; + double argv[8]; +}; + +static void horiz_to(cairo_t *cr, double x2) { + double x1, y1; + + cairo_get_current_point(cr, &x1, &y1); + cairo_line_to(cr, x2, y1); +} + +static void rel_horiz_to(cairo_t *cr, double x2) { + double x1, y1; + + cairo_get_current_point(cr, &x1, &y1); + cairo_line_to(cr, x1 + x2, y1); +} + +static void vert_to(cairo_t *cr, double y2) { + double x1, y1; + + cairo_get_current_point(cr, &x1, &y1); + cairo_line_to(cr, x1, y2); +} + +static void rel_vert_to(cairo_t *cr, double y2) { + double x1, y1; + + cairo_get_current_point(cr, &x1, &y1); + cairo_line_to(cr, x1, y1 + y2); +} + +static int command_init(struct command *cmd, char c) { + memset(cmd, '\0', sizeof(*cmd)); + + switch (c) { + case 'C': case 'c': cmd->stride = 6; break; + case 'Q': case 'q': + case 'S': case 's': cmd->stride = 4; break; + case 'M': case 'm': + case 'T': case 't': + case 'L': case 'l': cmd->stride = 2; break; + case 'H': case 'h': + case 'V': case 'v': cmd->stride = 1; break; + case 'Z': case 'z': cmd->stride = 0; break; + + default: + goto error; + } + + cmd->c = c; + cmd->first = 1; + cmd->argc = 0; + + return 0; + +error: + return -1; +} + +static int command_run(struct command *cmd, cairo_t *cr) { + if (cmd->stride == 0) { + if (cmd->argc > 0) { + goto error; + } + } else if (cmd->argc % cmd->stride != 0) { + goto error; + } + + switch (cmd->c) { + case 'M': + if (cmd->first) { + cairo_move_to(cr, cmd->argv[0], cmd->argv[1]); + } else { + cairo_line_to(cr, cmd->argv[0], cmd->argv[1]); + } + + break; + + case 'm': + if (cmd->first) { + cairo_rel_move_to(cr, cmd->argv[0], cmd->argv[1]); + } else { + cairo_rel_line_to(cr, cmd->argv[0], cmd->argv[1]); + } + + break; + + case 'L': + cairo_line_to(cr, cmd->argv[0], cmd->argv[1]); + break; + + case 'l': + cairo_rel_line_to(cr, cmd->argv[0], cmd->argv[1]); + break; + + case 'H': + horiz_to(cr, cmd->argv[0]); + break; + + case 'h': + rel_horiz_to(cr, cmd->argv[0]); + break; + + case 'V': + vert_to(cr, cmd->argv[0]); + break; + + case 'v': + rel_vert_to(cr, cmd->argv[0]); + break; + + case 'C': + cairo_curve_to(cr, + cmd->argv[0], cmd->argv[1], cmd->argv[2], + cmd->argv[3], cmd->argv[4], cmd->argv[5]); + + break; + + case 'c': + cairo_rel_curve_to(cr, + cmd->argv[0], cmd->argv[1], cmd->argv[2], + cmd->argv[3], cmd->argv[4], cmd->argv[5]); + + break; + + case 'Z': case 'z': + cairo_close_path(cr); + break; + + default: + goto error; + } + + return 0; + +error: + return -1; +} + +static void arg_init(struct arg *arg, size_t start, hexagram_path_arg_type type) { + memset(arg, '\0', sizeof(*arg)); + + arg->start = start; + arg->type = type; +} + +static int arg_end(struct arg *arg, struct command *cmd, size_t end, const char *buf, cairo_t *cr) { + if (end - arg->start >= sizeof(arg->buf)) { + goto error; + } + + strncpy(arg->buf, &buf[arg->start], end - arg->start); + + cmd->argv[cmd->argc++] = atof(arg->buf); + + if (cmd->argc == cmd->stride) { + if (command_run(cmd, cr) < 0) { + goto error; + } + + cmd->first = 0; + cmd->argc = 0; + } + + return 0; + +error: + return -1; +} + +int hexagram_path_run(cairo_t *cr, const char *data, size_t len) { + size_t i; + hexagram_path_state state = HEXAGRAM_PATH_STATE_NONE; + + struct command cmd = { .c = '\0', .argc = 0 }; + struct arg arg = { -1, -1, HEXAGRAM_PATH_ARG_NONE, "" }; + + for (i=0; i + +#include +#include + +int hexagram_pattern_init(hexagram_pattern *pattern, + double width, + double height, + const double color[3], + hexagram_pattern_op op, + const char *pathdata, + size_t len) { + cairo_t *cr; + + pattern->width = width; + pattern->height = height; + pattern->color[0] = color[0]; + pattern->color[1] = color[1]; + pattern->color[2] = color[2]; + + if ((pattern->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, + height)) == NULL) { + goto error_cairo_image_surface_create; + } + + if ((cr = cairo_create(pattern->surface)) == NULL) { + goto error_cairo_create; + } + + cairo_set_source_rgb(cr, color[0], color[1], color[2]); + + if (hexagram_path_run(cr, pathdata, len) < 0) { + goto error_path_run; + } + + switch (op) { + case HEXAGRAM_PATTERN_STROKE: + cairo_stroke(cr); + break; + + case HEXAGRAM_PATTERN_FILL: + cairo_fill(cr); + break; + } + + cairo_destroy(cr); + + return 0; + +error_path_run: + cairo_destroy(cr); + +error_cairo_create: + cairo_surface_destroy(pattern->surface); + +error_cairo_image_surface_create: + return -1; +} + +void hexagram_pattern_cleanup(hexagram_pattern *pattern) { + cairo_surface_destroy(pattern->surface); +} + +void hexagram_pattern_fill(hexagram_pattern *pattern, + cairo_t *cr, + const double matrix[6], + const double scale[2]) { + cairo_matrix_t m; + cairo_pattern_t *source; + + cairo_matrix_init(&m, + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5]); + + cairo_matrix_scale(&m, scale[0], scale[1]); + + cairo_set_source_surface(cr, pattern->surface, 0, 0); + + source = cairo_get_source(cr); + cairo_pattern_set_matrix(source, &m); + cairo_pattern_set_extend(source, CAIRO_EXTEND_REPEAT); + + cairo_fill(cr); +} diff --git a/src/speedo.c b/src/speedo.c index 91c74b2..4cc6fc4 100644 --- a/src/speedo.c +++ b/src/speedo.c @@ -4,74 +4,126 @@ #include #include -void hexagram_speedo_init(hexagram_speedo *speedo, - double x, - double y, - double radius) { - hexagram_gauge_init(&speedo->gauge, x, y, radius, - 232.0 * (M_PI / 180.0), - 488.0 * (M_PI / 180.0)); +#define SPEEDO_ANGLE_MIN 232.0 * (M_PI / 180.0) +#define SPEEDO_ANGLE_MAX 488.0 * (M_PI / 180.0) + +#define SPEEDO_FONT_FACE "Muli" + +static int draw_text(hexagram_speedo *speedo, + cairo_t *cr, + double x, + double y, + const char *text, + double size) { + cairo_text_extents_t extents; + + cairo_select_font_face(cr, + SPEEDO_FONT_FACE, + CAIRO_FONT_SLANT_ITALIC, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size(cr, speedo->dial.radius * size); + cairo_set_source_rgb(cr, 1, 0.4, 1); + + cairo_text_extents(cr, text, &extents); + + cairo_move_to(cr, + x - extents.width / 2, + y + extents.height / 4); + + cairo_show_text(cr, text); + + return 0; } -void hexagram_speedo_draw_face(hexagram_speedo *speedo, - cairo_t *cr) { - int i; +static int draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_speedo *speedo = (hexagram_speedo *)gauge; - cairo_select_font_face(cr, "Helvetica", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); + int speed; - cairo_set_font_size(cr, speedo->gauge.radius * 0.125); + if (hexagram_dial_draw_bg(&speedo->dial, cr) < 0) { + goto error_dial_draw_bg; + } + cairo_select_font_face(cr, + SPEEDO_FONT_FACE, + CAIRO_FONT_SLANT_ITALIC, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size(cr, speedo->dial.radius * 0.1); cairo_set_source_rgb(cr, 1, 1, 1); - cairo_arc(cr, - speedo->gauge.x, - speedo->gauge.y, - speedo->gauge.radius, - 0, - 2.0 * M_PI); + for (speed=0; speed<=(int)speedo->dial.max_value; speed += 5) { + double min_radius = speed % 10 == 0? 0.81: 0.82; - cairo_stroke(cr); - - /* - * Draw face numbers - */ - for (i=0; i<=180; i+=20) { - char text[5]; - - snprintf(text, 4, "%02d", i); - - hexagram_gauge_draw_number(&speedo->gauge, - cr, - 0.85, - i / 180.0, - text); + cairo_set_line_width(cr, speed % 10 == 0? 6.0: 2.0); + hexagram_dial_draw_mark(&speedo->dial, cr, min_radius, 0.87, (double)speed); } - for (i=0; i<=180; i+=2) { - hexagram_gauge_draw_mark(&speedo->gauge, - cr, - (i % 10)? 0.75: 0.7, - 0.8, - i / 180.0); + for (speed=0; speed<=(int)speedo->dial.max_value; speed += 20) { + char buf[8]; + + if (snprintf(buf, 8, "%d", speed) < 0) { + goto error_snprintf; + } + + hexagram_dial_draw_legend(&speedo->dial, cr, 0.68, speed, buf); } + + return 0; + +error_snprintf: +error_dial_draw_bg: + return -1; } -void hexagram_speedo_draw_needle(hexagram_speedo *speedo, - cairo_t *cr, - double rps) { - double kph = (2.00152 * rps * 3600) / 1000.0, - mph = kph * 0.621371; +static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_speedo *speedo = (hexagram_speedo *)gauge; + char buf[8]; - if (mph < 0) { - mph = 0; - } else if (mph > HEXAGRAM_SPEEDO_MAX_MPH) { - mph = HEXAGRAM_SPEEDO_MAX_MPH; + if (hexagram_dial_draw_fg(&speedo->dial, cr) < 0) { + goto error_dial_draw_fg; } - hexagram_gauge_draw_needle(&speedo->gauge, - cr, - 0.8, - mph / HEXAGRAM_SPEEDO_MAX_MPH); + if (snprintf(buf, 8, "%d", (int)speedo->dial.value) < 0) { + goto error_snprintf; + } + + draw_text(speedo, cr, gauge->x, gauge->y - speedo->dial.radius * 0.01, buf, 0.32); + + draw_text(speedo, cr, gauge->x, gauge->y + speedo->dial.radius * 0.2, "mph", 0.1); + + return 0; + +error_snprintf: +error_dial_draw_fg: + return -1; +} + +int hexagram_speedo_init(hexagram_speedo *speedo, + double x, + double y, + double radius, + double max_value, + hexagram_speedo_units units) { + if (hexagram_dial_init(&speedo->dial, + HEXAGRAM_DIAL_MAIN, + x, + y, + radius, + SPEEDO_ANGLE_MIN, + SPEEDO_ANGLE_MAX, + 0, + max_value) < 0) { + goto error_dial_init; + } + + speedo->dial.gauge.draw_bg = draw_bg; + speedo->dial.gauge.draw_fg = draw_fg; + speedo->units = units; + + return 0; + +error_dial_init: + return -1; } diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..3bec2d0 --- /dev/null +++ b/src/status.c @@ -0,0 +1,242 @@ +#include +#include + +#include + +#define STATUS_ICON_COUNT 14 +#define STATUS_ICON_WIDTH 48 +#define STATUS_ICON_HEIGHT 48 + +static hexagram_icon icons[STATUS_ICON_COUNT] = { + { + .id = HEXAGRAM_STATUS_SEATBELT, + .name = "belt", + .style_format = "path {fill: %1$s}", + .red = HEXAGRAM_STATUS_SEATBELT_UNFASTENED + }, + { + .id = HEXAGRAM_STATUS_PARKING_BEAM, + .name = "beams-parking", + .style_format = "path {stroke: %1$s; stroke-width: 12}", + .yellow = HEXAGRAM_STATUS_PARKING_BEAM_ON, + .red = HEXAGRAM_STATUS_PARKING_BEAM_FAULT + }, + { + .id = HEXAGRAM_STATUS_LOW_BEAM, + .name = "beams-low", + .style_format = "path {fill: %1$s; stroke: %1$s}", + .green = HEXAGRAM_STATUS_LOW_BEAM_ON, + .red = HEXAGRAM_STATUS_LOW_BEAM_FAULT + }, + { + .id = HEXAGRAM_STATUS_HIGH_BEAM, + .name = "beams-high", + .style_format = "path {fill: %1$s; stroke: %1$s}", + .blue = HEXAGRAM_STATUS_HIGH_BEAM_ON, + .red = HEXAGRAM_STATUS_HIGH_BEAM_FAULT + }, + { + .id = HEXAGRAM_STATUS_FOG_BEAM, + .name = "beams-fog", + .style_format = "#g9 path {fill: %1$s} #g5 path {stroke: %1$s}", + .green = HEXAGRAM_STATUS_FOG_BEAM_ON, + .red = HEXAGRAM_STATUS_FOG_BEAM_FAULT + }, + { + .id = HEXAGRAM_STATUS_STABILITY_CONTROL, + .name = "stability", + .style_format = "#path9, #path8, #path7, #path6, #path5 {fill: %1$s} #path10 {stroke: %1$s}", + .orange = HEXAGRAM_STATUS_STABILITY_CONTROL_OFF, + .red = HEXAGRAM_STATUS_STABILITY_CONTROL_FAULT + }, + { + .id = HEXAGRAM_STATUS_LANE_ASSIST, + .name = "lane-departure", + .style_format = "path {stroke: %1$s; stroke-width: 8}", + .orange = HEXAGRAM_STATUS_LANE_ASSIST_OFF, + .red = HEXAGRAM_STATUS_LANE_ASSIST_FAULT + }, + { + .id = HEXAGRAM_STATUS_PARKING_BRAKE, + .name = "parking", + .style_format = "#path6, #path5 {stroke: %1$s} circle {stroke: %1$s} #path7 {fill: %1$s}", + .red = HEXAGRAM_STATUS_PARKING_BRAKE_ON + }, + { + .id = HEXAGRAM_STATUS_FUEL, + .name = "fuel", + .style_format = "#g9 * {stroke: %1$s} #path9 {fill: %1$s}", + .orange = HEXAGRAM_STATUS_FUEL_LOW, + .red = HEXAGRAM_STATUS_FUEL_FAULT + }, + { + .id = HEXAGRAM_STATUS_WIPER_WASHER, + .name = "wiper-washer", + .style_format = "rect,path,circle{stroke:%1$s;}", + .orange = HEXAGRAM_STATUS_WIPER_WASHER_LOW, + .red = HEXAGRAM_STATUS_WIPER_WASHER_FAULT + }, + { + .id = HEXAGRAM_STATUS_COOLANT, + .name = "coolant", + .style_format = "path, circle, rect {fill: %1$s} #g9 path {stroke: %1$s}", + .orange = HEXAGRAM_STATUS_COOLANT_LOW, + .red = HEXAGRAM_STATUS_COOLANT_OVERHEAT + }, + { + .id = HEXAGRAM_STATUS_OIL, + .name = "oil", + .style_format = "path {fill: %1$s; stroke: %1$s}", + .orange = HEXAGRAM_STATUS_OIL_LOW, + .red = HEXAGRAM_STATUS_OIL_OVERHEAT + }, + { + .id = HEXAGRAM_STATUS_TPMS, + .name = "tpms", + .style_format = "path, circle {fill: %1$s}", + .orange = HEXAGRAM_STATUS_TPMS_WARNING, + .red = HEXAGRAM_STATUS_TPMS_FAULT + }, + { + .id = HEXAGRAM_STATUS_COLLISION, + .name = "collision", + .style_format = "path {stroke-width: 10px; stroke: %$1s}", + .red = HEXAGRAM_STATUS_COLLISION_DETECTED + } +}; + +static int update(hexagram_status_icon_box *box) { + cairo_t *cr; + double x, y; + size_t i; + + if ((cr = cairo_create(box->surface)) == NULL) { + goto error_cairo_create; + } + + cairo_set_source_rgba(cr, 0, 0, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, 0, 0, box->width, box->height); + cairo_fill(cr); + + x = 0; + y = box->height - STATUS_ICON_HEIGHT; + + for (i=0; iicons[i]; + int status = box->state[icon->id]; + + if (status && hexagram_icon_drawable(icon, status)) { + if (hexagram_icon_draw(icon, cr, x, y, status) < 0) { + goto error_icon_draw; + } + } + + x += STATUS_ICON_WIDTH; + + if (x >= box->width) { + x = 0; + y -= STATUS_ICON_HEIGHT; + } + } + + cairo_destroy(cr); + + return 0; + +error_icon_draw: + cairo_destroy(cr); + +error_cairo_create: + return -1; +} + +static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_status_icon_box *box = (hexagram_status_icon_box *)gauge; + + if (box->dirty) { + if (update(box) < 0) { + goto error_update; + } + + box->dirty = 0; + } + + cairo_set_source_surface(cr, box->surface, gauge->x, gauge->y); + cairo_rectangle(cr, gauge->x, gauge->y, box->width, box->height); + cairo_fill(cr); + + return 0; + +error_update: + return -1; +} + +int hexagram_status_icon_box_init(hexagram_status_icon_box *box, + double x, + double y, + double width, + double height) { + size_t i; + + box->gauge.x = x; + box->gauge.y = y; + box->gauge.draw_bg = NULL; + box->gauge.draw_fg = draw_fg; + + box->width = width; + box->height = height; + box->icons = icons; + box->dirty = 0; + + memset(&box->state, '\0', sizeof(box->state)); + + for (i=0; iicons[i], + STATUS_ICON_WIDTH, + STATUS_ICON_HEIGHT) < 0) { + goto error_icon_init; + } + } + + if ((box->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + box->width, + box->height)) == NULL) { + goto error_cairo_image_surface_create; + } + + return 0; + +error_cairo_image_surface_create: +error_icon_init: + do { + hexagram_icon_cleanup(&box->icons[i]); + } while (--i); + + return -1; +} + +void hexagram_status_icon_box_cleanup(hexagram_status_icon_box *box) { + size_t i; + + cairo_surface_destroy(box->surface); + + for (i=0; iicons[i]); + } + + free(box->icons); + + return; +} + +void hexagram_status_icon_set(hexagram_status_icon_box *box, + hexagram_status_type type, + int status) { + if (type < 0 || type >= HEXAGRAM_STATUS_COUNT) { + return; + } + + box->dirty = 1; + box->state[type] = status; +} diff --git a/src/svg.c b/src/svg.c new file mode 100644 index 0000000..8983377 --- /dev/null +++ b/src/svg.c @@ -0,0 +1,70 @@ +#include + +#include + +int hexagram_svg_render_to_surface(const char *path, + cairo_surface_t *surface, + double width, + double height, + const char *style) { + cairo_t *cr; + RsvgHandle *svg; + RsvgRectangle rect = {0, 0, width, height}; + GError *err = NULL; + + if ((cr = cairo_create(surface)) == NULL) { + goto error_cairo_create; + } + + if ((svg = rsvg_handle_new_from_file(path, &err)) == NULL) { + goto error_rsvg_handle_new_from_file; + } + + if (style) { + if (rsvg_handle_set_stylesheet(svg, (guint8 *)style, strlen(style), &err) != TRUE) { + goto error_rsvg_handle_set_stylesheet; + } + } + + if (rsvg_handle_render_layer(svg, cr, NULL, &rect, &err) != TRUE) { + goto error_rsvg_handle_render_layer; + } + + g_object_unref(svg); + cairo_destroy(cr); + + return 0; + +error_rsvg_handle_render_layer: +error_rsvg_handle_set_stylesheet: + g_object_unref(svg); + +error_rsvg_handle_new_from_file: + cairo_destroy(cr); + +error_cairo_create: + return -1; +} + +cairo_surface_t *hexagram_svg_render_to_image(const char *path, + double width, + double height, + const char *style) { + cairo_surface_t *surface; + + if ((surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height)) == NULL) { + goto error_cairo_image_surface_create; + } + + if (hexagram_svg_render_to_surface(path, surface, width, height, style) < 0) { + goto error_render_to_surface; + } + + return surface; + +error_render_to_surface: + cairo_surface_destroy(surface); + +error_cairo_image_surface_create: + return NULL; +} diff --git a/src/tacho.c b/src/tacho.c index 549cd68..bcf0e39 100644 --- a/src/tacho.c +++ b/src/tacho.c @@ -4,79 +4,274 @@ #include #include -void hexagram_tacho_init(hexagram_tacho *tacho, - double x, - double y, - double radius, - double redline) { - hexagram_gauge_init(&tacho->gauge, - x, - y, - radius, - 232.0 * (M_PI / 180.0), - 488.0 * (M_PI / 180.0)); +#define TACHO_ANGLE_MIN (232.0 * (M_PI / 180.0)) +#define TACHO_ANGLE_MAX (488.0 * (M_PI / 180.0)) - tacho->redline = redline; -} +#define TACHO_INTERVAL_MARK 500 +#define TACHO_INTERVAL_LEGEND 1000 -void hexagram_tacho_draw_face(hexagram_tacho *tacho, - cairo_t *cr) { - int i; +#define TACHO_FONT_FACE_LEGEND "Muli" +#define TACHO_FONT_FACE_GEAR "HEX:gon Bold Italic" - cairo_select_font_face(cr, "Helvetica", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); +static int draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_tacho *tacho = (hexagram_tacho *)gauge; + int rpm; - cairo_set_font_size(cr, tacho->gauge.radius * 0.125); + if (hexagram_dial_draw_bg(&tacho->dial, cr) < 0) { + goto error_dial_draw_bg; + } + + cairo_select_font_face(cr, + TACHO_FONT_FACE_LEGEND, + CAIRO_FONT_SLANT_ITALIC, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size(cr, tacho->dial.radius * 0.15); + cairo_set_source_rgb(cr, 1, 1, 1); + + for (rpm=0; rpm<=(int)tacho->dial.max_value; rpm+=TACHO_INTERVAL_MARK) { + double min_radius = (rpm % TACHO_INTERVAL_LEGEND) == 0? 0.81: 0.82; + + if (rpm >= tacho->redline) { + cairo_set_source_rgb(cr, 0.6, 0.1, 0.1); + } + + cairo_set_line_width(cr, (rpm % TACHO_INTERVAL_LEGEND) == 0? 6.0: 2.0); + + hexagram_dial_draw_mark(&tacho->dial, cr, min_radius, 0.87, rpm); + } cairo_set_source_rgb(cr, 1, 1, 1); - cairo_arc(cr, - tacho->gauge.x, - tacho->gauge.y, - tacho->gauge.radius, - 0, - 2.0 * M_PI); + for (rpm=0; rpm<=(int)tacho->dial.max_value; rpm+=TACHO_INTERVAL_LEGEND) { + char buf[8]; - cairo_stroke(cr); - - /* - * Draw face numbers - */ - for (i=0; i<=80; i+=10) { - char text[4]; - - snprintf(text, 3, "%02d", i); - - hexagram_gauge_draw_number(&tacho->gauge, - cr, - 0.85, - i / 80.0, - text); - } - - for (i=0; i<=80; i++) { - if (i * 100 >= tacho->redline) { - cairo_set_source_rgb(cr, 1, 0, 0); + if (rpm >= tacho->redline) { + cairo_set_source_rgb(cr, 1.0, 0.1, 0.1); } - hexagram_gauge_draw_mark(&tacho->gauge, - cr, - i % 5? 0.75: 0.7, - 0.8, - i / 80.0); - } -} + snprintf(buf, sizeof(buf), "%d", (int)(rpm / TACHO_INTERVAL_LEGEND)); -void hexagram_tacho_draw_needle(hexagram_tacho *tacho, - cairo_t *cr, - double rpm) { - if (rpm > 8000) { - rpm = 8000; + hexagram_dial_draw_legend(&tacho->dial, cr, 0.675, rpm, buf); } - hexagram_gauge_draw_needle(&tacho->gauge, - cr, - 0.8, - rpm / 8000); + return 0; + +error_dial_draw_bg: + return -1; +} + +static int gear_text(hexagram_tacho *tacho, char *buf, size_t len) { + const char *formats[3] = { + "S%s", + "M%s", + "%s" + }; + + const char *format; + + if (tacho->gear < HEXAGRAM_TRANS_GEAR_S || tacho->gear > HEXAGRAM_TRANS_GEAR_10) { + return 0; + } + + if (tacho->gear >= HEXAGRAM_TRANS_GEAR_1) { + switch (tacho->shift_mode) { + case HEXAGRAM_TRANS_SHIFT_SPORT: + case HEXAGRAM_TRANS_SHIFT_RACE: + format = formats[0]; + break; + + case HEXAGRAM_TRANS_SHIFT_MANUAL: + format = formats[1]; + break; + + default: + format = formats[2]; + break; + } + } else { + format = formats[2]; + } + + return snprintf(buf, len, format, hexagram_trans_gear_str(tacho->gear)); +} + +static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_tacho *tacho = (hexagram_tacho *)gauge; + cairo_text_extents_t extents; + char buf[8]; + + if (hexagram_dial_draw_fg(&tacho->dial, cr) < 0) { + goto error_dial_draw_fg; + } + + if (gear_text(tacho, buf, sizeof(buf)) < 0) { + goto error_gear_text; + } + + cairo_select_font_face(cr, + TACHO_FONT_FACE_GEAR, + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + + cairo_set_font_size(cr, tacho->dial.radius * 0.4); + cairo_set_source_rgb(cr, 1, 0.4, 1); + + cairo_text_extents(cr, buf, &extents); + + cairo_move_to(cr, + gauge->x - extents.width / 2, + gauge->y + extents.height / 2); + + cairo_show_text(cr, buf); + + return 0; + +error_gear_text: +error_dial_draw_fg: + return -1; +} + +int hexagram_tacho_init(hexagram_tacho *tacho, + double x, + double y, + double radius, + double redline, + double max_rpm) { + if (hexagram_dial_init(&tacho->dial, + HEXAGRAM_DIAL_MAIN, + x, + y, + radius, + TACHO_ANGLE_MIN, + TACHO_ANGLE_MAX, + 0, + max_rpm) < 0) { + goto error_dial_init; + } + + tacho->dial.gauge.draw_bg = draw_bg; + tacho->dial.gauge.draw_fg = draw_fg; + + tacho->redline = redline; + tacho->shift_mode = HEXAGRAM_TRANS_SHIFT_NORMAL; + tacho->gear = HEXAGRAM_TRANS_GEAR_N; + + return 0; + +error_dial_init: + return -1; +} + +#define LIGHT_LEVEL_COUNT 5 +#define LIGHT_WIDTH 48 +#define LIGHT_HEIGHT 12 +#define LIGHT_SPACING 8 +#define LIGHT_LOW 0.25 + +static double light_colors[LIGHT_LEVEL_COUNT][3] = { + { 0.0, 1.0, 0.0 }, + { 0.6, 1.0, 0.0 }, + { 1.0, 1.0, 0.0 }, + { 1.0, 0.6, 0.0 }, + { 1.0, 0.0, 0.0 } +}; + +static int light_levels[LIGHT_LEVEL_COUNT][2] = { + { 0, 8 }, + { 1, 7 }, + { 2, 6 }, + { 3, 5 }, + { 4, 4 } +}; + +static int shift_indicator_draw_level(hexagram_tacho_shift_indicator *shift, + cairo_t *cr, + int level, + int on) { + int max_level = LIGHT_LEVEL_COUNT - 1; + double r, g, b; + int i; + + if (level < 0) { + level = 0; + } else if (level >= max_level) { + level = max_level; + } + + r = light_colors[level][0]; + g = light_colors[level][1]; + b = light_colors[level][2]; + + if (!on) { + r *= LIGHT_LOW; + g *= LIGHT_LOW; + b *= LIGHT_LOW; + } + + cairo_set_source_rgb(cr, r, g, b); + + for (i=0; i<2; i++) { + int l = light_levels[level][i]; + double x = l * (LIGHT_WIDTH + LIGHT_SPACING); + + cairo_rectangle(cr, + shift->gauge.x + x, + shift->gauge.y, + LIGHT_WIDTH, + LIGHT_HEIGHT); + + cairo_fill(cr); + } + + return 0; +} + +static int shift_indicator_draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_tacho_shift_indicator *shift = (hexagram_tacho_shift_indicator *)gauge; + int level; + + for (level=0; levelrpm - shift->rpm_min, + adj_max = shift->rpm_shift - shift->rpm_min; + + int level = (int)((adj / adj_max) * LIGHT_LEVEL_COUNT), + i; + + if (shift->rpm < shift->rpm_min) { + return 0; + } + + for (i=0; i= i); + } + + return 0; +} + +int hexagram_tacho_shift_indicator_init(hexagram_tacho_shift_indicator *shift, + double x, + double y, + double rpm_min, + double rpm_shift) { + hexagram_gauge_init(&shift->gauge, + x, + y, + shift_indicator_draw_bg, + shift_indicator_draw_fg); + + shift->rpm_min = rpm_min; + shift->rpm_shift = rpm_shift; + shift->rpm = 0; + + return 0; } diff --git a/src/temp.c b/src/temp.c new file mode 100644 index 0000000..8414cdb --- /dev/null +++ b/src/temp.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#include +#include + +#define ICON_ID HEXAGRAM_STATUS_COLD +#define ICON_NAME "cold" +#define ICON_STYLE "path {stroke: %1$s; stroke-width: 8}" +#define ICON_WIDTH 40 +#define ICON_HEIGHT 40 +#define ICON_PADDING 16 + +#define COLD 1 +#define COLD_THRESHOLD 4.0 /* °C */ + +static ssize_t format_text(hexagram_text *text, char *buf, size_t len) { + hexagram_temp *temp = (hexagram_temp *)text; + + return snprintf(buf, len, "%.1f°C", temp->value); +} + +static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_temp *temp = (hexagram_temp *)gauge; + cairo_text_extents_t extents; + double text_x_offset = 0.0; + + if (temp->text.format_text(&temp->text, temp->text.buf, sizeof(temp->text.buf)) < 0) { + goto error_format_text; + } + + cairo_select_font_face(cr, + HEXAGRAM_TEXT_FONT_FACE, + HEXAGRAM_TEXT_FONT_SLANT, + HEXAGRAM_TEXT_FONT_WEIGHT); + + cairo_text_extents(cr, temp->text.buf, &extents); + + if (temp->value <= COLD_THRESHOLD) { + extents.width += ICON_WIDTH + ICON_PADDING; + } + + switch (temp->text.align) { + case HEXAGRAM_TEXT_LEFT: + break; + + case HEXAGRAM_TEXT_MIDDLE: + text_x_offset = gauge->x - extents.width / 2.0; + break; + + case HEXAGRAM_TEXT_RIGHT: + text_x_offset = gauge->x - extents.width; + break; + } + + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_move_to(cr, text_x_offset, gauge->y); + cairo_show_text(cr, temp->text.buf); + + if (temp->value <= COLD_THRESHOLD) { + int icon_y_offset = abs(ICON_HEIGHT - HEXAGRAM_TEXT_FONT_SIZE) * 2; + + if (hexagram_icon_draw(&temp->icon, + cr, + text_x_offset + extents.width - ICON_WIDTH, + gauge->y - icon_y_offset, + COLD) < 0) { + goto error_icon_draw; + } + } + + return 0; + +error_icon_draw: +error_format_text: + return -1; +} + +int hexagram_temp_init(hexagram_temp *temp, + double x, + double y, + hexagram_text_align align, + hexagram_temp_units units) { + if (hexagram_text_init(&temp->text, + x, + y, + align, + format_text) < 0) { + goto error_text_init; + } + + memset(&temp->icon, '\0', sizeof(hexagram_icon)); + + temp->text.gauge.draw_fg = draw_fg; + temp->icon.id = ICON_ID; + temp->icon.name = ICON_NAME; + temp->icon.style_format = ICON_STYLE; + temp->icon.white = COLD; + + if (hexagram_icon_init(&temp->icon, ICON_WIDTH, ICON_HEIGHT) < 0) { + goto error_icon_init; + } + + temp->units = units; + temp->value = 0; + + return 0; + +error_icon_init: +error_text_init: + return -1; +} + +void hexagram_temp_cleanup(hexagram_temp *temp) { + hexagram_icon_cleanup(&temp->icon); +} diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..feea602 --- /dev/null +++ b/src/text.c @@ -0,0 +1,64 @@ +#include +#include + +#include + +#define FONT_FACE "Muli" +#define FONT_SLANT CAIRO_FONT_SLANT_NORMAL +#define FONT_WEIGHT CAIRO_FONT_WEIGHT_BOLD +#define FONT_SIZE 24 + +static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_text *text = (hexagram_text *)gauge; + cairo_text_extents_t extents; + + cairo_set_source_rgb(cr, 1, 1, 1); + + cairo_select_font_face(cr, + FONT_FACE, + FONT_SLANT, + FONT_WEIGHT); + + text->format_text(text, text->buf, sizeof(text->buf)); + + cairo_set_font_size(cr, FONT_SIZE); + + cairo_text_extents(cr, text->buf, &extents); + + switch (text->align) { + case HEXAGRAM_TEXT_LEFT: + cairo_move_to(cr, text->gauge.x, text->gauge.y); + break; + + case HEXAGRAM_TEXT_MIDDLE: + cairo_move_to(cr, text->gauge.x - extents.width / 2, text->gauge.y); + break; + + case HEXAGRAM_TEXT_RIGHT: + cairo_move_to(cr, text->gauge.x - extents.width, text->gauge.y); + break; + } + + cairo_show_text(cr, text->buf); + + return 0; +} + +int hexagram_text_init(hexagram_text *text, + double x, + double y, + hexagram_text_align align, + hexagram_text_format_callback *format_text) { + hexagram_gauge_init(&text->gauge, + x, + y, + NULL, + draw_fg); + + text->align = align; + text->format_text = format_text; + + memset(text->buf, '\0', sizeof(text->buf)); + + return 0; +} diff --git a/src/thermo.c b/src/thermo.c index f913743..7ace25e 100644 --- a/src/thermo.c +++ b/src/thermo.c @@ -3,93 +3,85 @@ #include #include -void hexagram_thermo_init(hexagram_thermo *thermo, - double x, - double y, - double radius, - double redline) { - hexagram_gauge_init(&thermo->gauge, x, y, radius, - 300.0 * (M_PI / 180.0), - 420.0 * (M_PI / 180.0)); +#define RAD (M_PI / 180.0) - thermo->redline = redline; -} +#define ANGLE_MIN (144.0 * RAD) +#define ANGLE_MAX (216.0 * RAD) -void hexagram_thermo_draw_face(hexagram_thermo *thermo, - cairo_t *cr) { +#define LEVEL_MIN 48 +#define LEVEL_WARN 116 +#define LEVEL_MAX 127 + +#define FONT_FACE "Muli" + +static int draw_bg(hexagram_gauge *gauge, cairo_t *cr) { + hexagram_thermo *thermo = (hexagram_thermo *)gauge; + double incr; int i; - cairo_select_font_face(cr, "Helvetica", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); + if (hexagram_dial_draw_bg(&thermo->dial, cr) < 0) { + goto error_dial_draw_bg; + } - cairo_set_font_size(cr, thermo->gauge.radius * 0.2); + cairo_select_font_face(cr, + FONT_FACE, + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size(cr, thermo->dial.radius * 0.1); cairo_set_source_rgb(cr, 1, 1, 1); + hexagram_dial_draw_legend(&thermo->dial, cr, 0.75, thermo->dial.min_value, "C"); - cairo_arc(cr, - thermo->gauge.x, - thermo->gauge.y, - thermo->gauge.radius, - 0, - 2.0 * M_PI); + cairo_set_source_rgb(cr, 1, 0, 0); + hexagram_dial_draw_legend(&thermo->dial, cr, 0.75, thermo->dial.max_value, "H"); - cairo_stroke(cr); + cairo_set_line_width(cr, 6.0); - /* - * Draw face numbers - */ - cairo_move_to(cr, - thermo->gauge.x - 0.8 * thermo->gauge.radius, - thermo->gauge.y - 0.1 * thermo->gauge.radius); + incr = (thermo->dial.max_value - thermo->dial.min_value) / 8.0; - cairo_show_text(cr, "120"); + for (i=0; i<9; i++) { + double level = thermo->dial.min_value + i * incr; - cairo_move_to(cr, - thermo->gauge.x - 0.15 * thermo->gauge.radius, - thermo->gauge.y - 0.5 * thermo->gauge.radius); - - cairo_show_text(cr, "190"); - - cairo_move_to(cr, - thermo->gauge.x + 0.5 * thermo->gauge.radius, - thermo->gauge.y - 0.1 * thermo->gauge.radius); - - cairo_show_text(cr, "260"); - - /* - * Draw gauge graduations - */ - for (i=0; i<=16; i++) { - double min_radius = (i >= 14)? - (i % 2? 0.8: 0.7): - (i % 4? 0.8: 0.7); - - if ((i * 8.23529411) + 120 >= thermo->redline) { - cairo_set_source_rgb(cr, 1, 0, 0); + if (level >= LEVEL_WARN) { + cairo_set_source_rgb(cr, 0.8, 0, 0); + } else { + cairo_set_source_rgb(cr, 1, 1, 1); } - hexagram_gauge_draw_mark(&thermo->gauge, - cr, - min_radius, - 0.90, - i / 16.0); - } -} + hexagram_dial_draw_mark(&thermo->dial, cr, 0.83, 0.87, level); -void hexagram_thermo_draw_needle(hexagram_thermo *thermo, - cairo_t *cr, - double temp) { - double fahrenheit = temp * (9.0 / 5.0) + 32.0; - - if (fahrenheit < 120) { - fahrenheit = 120; - } else if (fahrenheit > 260) { - fahrenheit = 260; + level += incr; } - hexagram_gauge_draw_needle(&thermo->gauge, - cr, - 0.9, - (fahrenheit - 120) / 140.0); + return 0; + +error_dial_draw_bg: + return -1; +} + +int hexagram_thermo_init(hexagram_thermo *thermo, + double x, + double y, + double radius) { + if (hexagram_dial_init(&thermo->dial, + HEXAGRAM_DIAL_BOTTOM, + x, + y, + radius, + ANGLE_MIN, + ANGLE_MAX, + LEVEL_MIN, + LEVEL_MAX) < 0) { + goto error_dial_init; + } + + thermo->dial.gauge.draw_bg = draw_bg; + thermo->dial.value = LEVEL_MIN; + thermo->level_warn = LEVEL_WARN; + + return 0; + +error_dial_init: + return -1; } diff --git a/src/trans.c b/src/trans.c new file mode 100644 index 0000000..92112ba --- /dev/null +++ b/src/trans.c @@ -0,0 +1,28 @@ +#include + +#include + +char *hexagram_trans_gear_str(hexagram_trans_gear gear) { + static char *gears[15] = { + "S", "D", "P", "R", "N", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "10" + }; + + if (gear < HEXAGRAM_TRANS_GEAR_S || gear > HEXAGRAM_TRANS_GEAR_10) { + return NULL; + } + + return gears[gear + 4]; +} + +char *hexagram_trans_shift_mode_str(hexagram_trans_shift_mode mode) { + static char *modes[6] = { + "normal", "comfort", "eco", "sport", "race", "manual" + }; + + if (mode < HEXAGRAM_TRANS_SHIFT_NORMAL || mode > HEXAGRAM_TRANS_SHIFT_MANUAL) { + return NULL; + } + + return modes[mode]; +}