Port Python to C

This commit is contained in:
XANTRONIX Development 2024-01-11 17:39:52 -05:00
parent 25664644bf
commit 1bda9eda26
38 changed files with 2859 additions and 578 deletions

View file

@ -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

View file

@ -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;

View file

@ -8,7 +8,47 @@
#include <cairo-svg.h>
#include <hexagram/cluster.h>
#include <hexagram/path.h>
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;
}

32
include/hexagram/clock.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef _HEXAGRAM_CLOCK_H
#define _HEXAGRAM_CLOCK_H
#include <stdint.h>
#include <time.h>
#include <hexagram/text.h>
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 */

View file

@ -3,48 +3,56 @@
#include <cairo.h>
#include <hexagram/list.h>
#include <hexagram/can.h>
#include <hexagram/tacho.h>
#include <hexagram/pattern.h>
#include <hexagram/speedo.h>
#include <hexagram/thermo.h>
#include <hexagram/fuel.h>
#include <hexagram/mfd.h>
#include <hexagram/tacho.h>
#include <hexagram/thermo.h>
#include <hexagram/odo.h>
#include <hexagram/temp.h>
#include <hexagram/clock.h>
#include <hexagram/status.h>
#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 */

73
include/hexagram/dial.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef _HEXAGRAM_DIAL_H
#define _HEXAGRAM_DIAL_H
#include <hexagram/gauge.h>
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 */

View file

@ -1,26 +1,16 @@
#ifndef _HEXAGRAM_FUEL_H
#define _HEXAGRAM_FUEL_H
#include <cairo.h>
#include <hexagram/gauge.h>
#include <hexagram/dial.h>
typedef struct _hexagram_fuel {
hexagram_gauge gauge;
double redline;
hexagram_dial dial;
double warn_level;
} hexagram_fuel;
void hexagram_fuel_init(hexagram_fuel *fuel,
int 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);
double radius);
#endif /* _HEXAGRAM_FUEL_H */

View file

@ -3,35 +3,26 @@
#include <cairo.h>
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 */

47
include/hexagram/icon.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef _HEXAGRAM_ICON_H
#define _HEXAGRAM_ICON_H
#include <sys/types.h>
#include <cairo.h>
#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 */

23
include/hexagram/odo.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef _HEXAGRAM_ODO_H
#define _HEXAGRAM_ODO_H
#include <hexagram/text.h>
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 */

21
include/hexagram/path.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef _HEXAGRAM_PATH_H
#define _HEXAGRAM_PATH_H
#include <sys/types.h>
#include <cairo.h>
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 */

View file

@ -0,0 +1,34 @@
#ifndef _HEXAGRAM_PATTERN_H
#define _HEXAGRAM_PATTERN_H
#include <sys/types.h>
#include <cairo.h>
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 */

View file

@ -1,27 +1,25 @@
#ifndef _HEXAGRAM_SPEEDO_H
#define _HEXAGRAM_SPEEDO_H
#include <cairo.h>
#include <math.h>
#include <hexagram/gauge.h>
#include <hexagram/dial.h>
#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,
int 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);
double radius,
double max_value,
hexagram_speedo_units units);
#endif /* _HEXAGRAM_SPEEDO_H */

173
include/hexagram/status.h Normal file
View file

@ -0,0 +1,173 @@
#ifndef _HEXAGRAM_STATUS_H
#define _HEXAGRAM_STATUS_H
#include <cairo.h>
#include <hexagram/icon.h>
#include <hexagram/gauge.h>
#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 */

17
include/hexagram/svg.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _HEXAGRAM_SVG_H
#define _HEXAGRAM_SVG_H
#include <cairo.h>
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 */

View file

@ -1,26 +1,38 @@
#ifndef _HEXAGRAM_TACHO_H
#define _HEXAGRAM_TACHO_H
#include <cairo.h>
#include <math.h>
#include <hexagram/trans.h>
#include <hexagram/dial.h>
#include <hexagram/gauge.h>
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,
typedef struct _hexagram_tacho_shift_indicator {
hexagram_gauge gauge;
double rpm_min,
rpm_shift,
rpm;
} hexagram_tacho_shift_indicator;
int hexagram_tacho_init(hexagram_tacho *tacho,
double x,
double y,
double radius,
double redline);
double redline,
double rpm_max);
void hexagram_tacho_draw_face(hexagram_tacho *tacho,
cairo_t *cr);
void hexagram_tacho_draw_needle(hexagram_tacho *tacho,
cairo_t *cr,
double rpm);
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 */

27
include/hexagram/temp.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef _HEXAGRAM_TEMP_H
#define _HEXAGRAM_TEMP_H
#include <hexagram/text.h>
#include <hexagram/icon.h>
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 */

37
include/hexagram/text.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _HEXAGRAM_TEXT_H
#define _HEXAGRAM_TEXT_H
#include <sys/types.h>
#include <cairo.h>
#include <hexagram/gauge.h>
#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 */

View file

@ -3,24 +3,18 @@
#include <cairo.h>
#include <hexagram/gauge.h>
#include <hexagram/dial.h>
#include <hexagram/temp.h>
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,
int 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);
double radius);
#endif /* _HEXAGRAM_THERMO_H */

35
include/hexagram/trans.h Normal file
View file

@ -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 */

View file

@ -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

54
src/clock.c Normal file
View file

@ -0,0 +1,54 @@
#include <stdio.h>
#include <time.h>
#include <hexagram/clock.h>
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;
}

View file

@ -3,74 +3,68 @@
#include <hexagram/cluster.h>
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;
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;
}
case 0x288: {
cluster->state.temp = 0.75 * (double)(frame->data[1] - 48);
return 1;
if (hexagram_speedo_init(&cluster->speedo,
CLUSTER_HEIGHT / 2,
CLUSTER_HEIGHT / 2,
CLUSTER_HEIGHT / 2,
SPEEDO_MAX,
SPEEDO_UNITS) < 0) {
goto error_speedo_init;
}
case 0x320: {
cluster->state.fuel = (double)(frame->data[2] & 0xf) / 15.0;
return 1;
if (hexagram_fuel_init(&cluster->fuel,
CLUSTER_HEIGHT / 2,
CLUSTER_HEIGHT / 2,
CLUSTER_HEIGHT / 2) < 0) {
goto error_fuel_init;
}
case 0x5a0: {
cluster->state.rps = 0.001 * (double)(frame->data[1]
| (frame->data[2] << 8));
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;
}
return 1;
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);
hexagram_thermo_draw_needle(&cluster->thermo, cr,
cluster->state.temp);
hexagram_fuel_draw_needle(&cluster->fuel, cr,
cluster->state.fuel);
if ((cluster = malloc(sizeof(*cluster))) == NULL) {
goto error_malloc_cluster;
}
hexagram_cluster_init(cluster, rpm_min, rpm_redline, rpm_max);
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; i<HEXAGRAM_CLUSTER_GAUGE_COUNT; i++) {
if (cluster->gauges[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; i<HEXAGRAM_CLUSTER_GAUGE_COUNT; i++) {
if (cluster->gauges[i]->draw_fg) {
cluster->gauges[i]->draw_fg(cluster->gauges[i], cr);
}
}
}

305
src/dial.c Normal file
View file

@ -0,0 +1,305 @@
#include <stdlib.h>
#include <math.h>
#include <hexagram/dial.h>
#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;
}

View file

@ -1,88 +1,87 @@
#include <stdlib.h>
#include <math.h>
#include <hexagram/gauge.h>
#include <hexagram/fuel.h>
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
#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;
}
void hexagram_fuel_draw_face(hexagram_fuel *fuel,
cairo_t *cr) {
int i;
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);
}
hexagram_dial_draw_mark(&fuel->dial, cr, min_radius, 0.87, level);
}
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;
return 0;
error_dial_draw_bg:
return -1;
}
cairo_stroke(cr);
hexagram_gauge_draw_needle(&fuel->gauge,
cr,
0.9,
level);
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;
}

View file

@ -6,87 +6,10 @@
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) {
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);
gauge->draw_bg = draw_bg;
gauge->draw_fg = draw_fg;
}

151
src/icon.c Normal file
View file

@ -0,0 +1,151 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <hexagram/svg.h>
#include <hexagram/icon.h>
#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; c<HEXAGRAM_ICON_COLOR_COUNT; c++) {
icon->surfaces[c] = NULL;
}
for (i=0; i<ICON_DIR_COUNT; i++) {
size_t len = strlen(icon_dirs[i]) + 1 + strlen(icon->name) + 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; c<HEXAGRAM_ICON_COLOR_COUNT; c++) {
if (!icon->colors[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; c<HEXAGRAM_ICON_COLOR_COUNT; c++) {
if (icon->surfaces[c]) {
cairo_surface_destroy(icon->surfaces[c]);
}
}
}
int hexagram_icon_drawable(hexagram_icon *icon, int status) {
int color;
for (color=0; color<HEXAGRAM_ICON_COLOR_COUNT; color++) {
if (icon->colors[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; color<HEXAGRAM_ICON_COLOR_COUNT; color++) {
if (icon->colors[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;
}

31
src/odo.c Normal file
View file

@ -0,0 +1,31 @@
#include <stdio.h>
#include <hexagram/odo.h>
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;
}

286
src/path.c Normal file
View file

@ -0,0 +1,286 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hexagram/path.h>
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<len; i++) {
char c = data[i];
switch (state) {
case HEXAGRAM_PATH_STATE_NONE:
if (is_alpha(c)) {
command_init(&cmd, c);
state = HEXAGRAM_PATH_STATE_COMMAND;
} else {
goto error;
}
break;
case HEXAGRAM_PATH_STATE_COMMAND:
if (is_alpha(c)) {
if (command_run(&cmd, cr) < 0) {
goto error;
}
command_init(&cmd, c);
} else if (is_num_start(c)) {
arg_init(&arg, i, HEXAGRAM_PATH_ARG_INT);
state = HEXAGRAM_PATH_STATE_ARG;
} else if (c == '.') {
arg_init(&arg, i, HEXAGRAM_PATH_ARG_FLOAT);
state = HEXAGRAM_PATH_STATE_ARG;
} else if (!is_sep(c)) {
goto error;
}
break;
case HEXAGRAM_PATH_STATE_ARG:
if (is_alpha(c)) {
if (arg_end(&arg, &cmd, i, data, cr) < 0) {
goto error;
}
command_init(&cmd, c);
state = HEXAGRAM_PATH_STATE_COMMAND;
} else if (is_num(c)) {
arg.end = i;
} else if (is_sep(c)) {
if (arg_end(&arg, &cmd, i, data, cr) < 0) {
goto error;
}
state = HEXAGRAM_PATH_STATE_COMMAND;
} else if (c == '-') {
arg_init(&arg, i, HEXAGRAM_PATH_ARG_INT);
} else if (c == '.') {
if (arg.type == HEXAGRAM_PATH_ARG_FLOAT) {
goto error;
} else {
arg.type = HEXAGRAM_PATH_ARG_FLOAT;
}
}
break;
}
}
if (state == HEXAGRAM_PATH_STATE_ARG) {
if (arg_end(&arg, &cmd, i, data, cr) < 0) {
goto error;
}
} else if (state == HEXAGRAM_PATH_STATE_COMMAND) {
if (command_run(&cmd, cr) < 0) {
goto error;
}
}
return 0;
error:
return -1;
}

85
src/pattern.c Normal file
View file

@ -0,0 +1,85 @@
#include <stdlib.h>
#include <hexagram/path.h>
#include <hexagram/pattern.h>
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);
}

View file

@ -4,74 +4,126 @@
#include <hexagram/gauge.h>
#include <hexagram/speedo.h>
void hexagram_speedo_init(hexagram_speedo *speedo,
#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,
double radius) {
hexagram_gauge_init(&speedo->gauge, x, y, radius,
232.0 * (M_PI / 180.0),
488.0 * (M_PI / 180.0));
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,
for (speed=0; speed<=(int)speedo->dial.max_value; speed += 5) {
double min_radius = speed % 10 == 0? 0.81: 0.82;
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 (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;
}
static int draw_fg(hexagram_gauge *gauge, cairo_t *cr) {
hexagram_speedo *speedo = (hexagram_speedo *)gauge;
char buf[8];
if (hexagram_dial_draw_fg(&speedo->dial, cr) < 0) {
goto error_dial_draw_fg;
}
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,
2.0 * M_PI);
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);
max_value) < 0) {
goto error_dial_init;
}
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);
}
}
speedo->dial.gauge.draw_bg = draw_bg;
speedo->dial.gauge.draw_fg = draw_fg;
speedo->units = units;
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;
return 0;
if (mph < 0) {
mph = 0;
} else if (mph > HEXAGRAM_SPEEDO_MAX_MPH) {
mph = HEXAGRAM_SPEEDO_MAX_MPH;
}
hexagram_gauge_draw_needle(&speedo->gauge,
cr,
0.8,
mph / HEXAGRAM_SPEEDO_MAX_MPH);
error_dial_init:
return -1;
}

242
src/status.c Normal file
View file

@ -0,0 +1,242 @@
#include <stdlib.h>
#include <string.h>
#include <hexagram/status.h>
#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; i<STATUS_ICON_COUNT; i++) {
hexagram_icon *icon = &box->icons[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; i<STATUS_ICON_COUNT; i++) {
if (hexagram_icon_init(&box->icons[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; i<STATUS_ICON_COUNT; i++) {
hexagram_icon_cleanup(&box->icons[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;
}

70
src/svg.c Normal file
View file

@ -0,0 +1,70 @@
#include <librsvg/rsvg.h>
#include <hexagram/svg.h>
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;
}

View file

@ -4,79 +4,274 @@
#include <hexagram/gauge.h>
#include <hexagram/tacho.h>
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
#define TACHO_FONT_FACE_LEGEND "Muli"
#define TACHO_FONT_FACE_GEAR "HEX:gon Bold Italic"
static int draw_bg(hexagram_gauge *gauge, cairo_t *cr) {
hexagram_tacho *tacho = (hexagram_tacho *)gauge;
int rpm;
if (hexagram_dial_draw_bg(&tacho->dial, cr) < 0) {
goto error_dial_draw_bg;
}
void hexagram_tacho_draw_face(hexagram_tacho *tacho,
cairo_t *cr) {
int i;
cairo_select_font_face(cr,
TACHO_FONT_FACE_LEGEND,
CAIRO_FONT_SLANT_ITALIC,
CAIRO_FONT_WEIGHT_BOLD);
cairo_select_font_face(cr, "Helvetica",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, tacho->dial.radius * 0.15);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_set_font_size(cr, tacho->gauge.radius * 0.125);
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,
for (rpm=0; rpm<=(int)tacho->dial.max_value; rpm+=TACHO_INTERVAL_LEGEND) {
char buf[8];
if (rpm >= tacho->redline) {
cairo_set_source_rgb(cr, 1.0, 0.1, 0.1);
}
snprintf(buf, sizeof(buf), "%d", (int)(rpm / TACHO_INTERVAL_LEGEND));
hexagram_dial_draw_legend(&tacho->dial, cr, 0.675, rpm, buf);
}
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,
2.0 * M_PI);
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);
max_rpm) < 0) {
goto error_dial_init;
}
for (i=0; i<=80; i++) {
if (i * 100 >= tacho->redline) {
cairo_set_source_rgb(cr, 1, 0, 0);
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;
}
hexagram_gauge_draw_mark(&tacho->gauge,
cr,
i % 5? 0.75: 0.7,
0.8,
i / 80.0);
}
}
#define LIGHT_LEVEL_COUNT 5
#define LIGHT_WIDTH 48
#define LIGHT_HEIGHT 12
#define LIGHT_SPACING 8
#define LIGHT_LOW 0.25
void hexagram_tacho_draw_needle(hexagram_tacho *tacho,
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,
double rpm) {
if (rpm > 8000) {
rpm = 8000;
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;
}
hexagram_gauge_draw_needle(&tacho->gauge,
cr,
0.8,
rpm / 8000);
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; level<LIGHT_LEVEL_COUNT; level++) {
shift_indicator_draw_level(shift, cr, level, 0);
}
return 0;
}
static int shift_indicator_draw_fg(hexagram_gauge *gauge, cairo_t *cr) {
hexagram_tacho_shift_indicator *shift = (hexagram_tacho_shift_indicator *)gauge;
double adj = shift->rpm - 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<LIGHT_LEVEL_COUNT; i++) {
shift_indicator_draw_level(shift, cr, i, level >= 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;
}

117
src/temp.c Normal file
View file

@ -0,0 +1,117 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hexagram/temp.h>
#include <hexagram/status.h>
#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);
}

64
src/text.c Normal file
View file

@ -0,0 +1,64 @@
#include <stdlib.h>
#include <string.h>
#include <hexagram/text.h>
#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;
}

View file

@ -3,93 +3,85 @@
#include <hexagram/gauge.h>
#include <hexagram/thermo.h>
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_stroke(cr);
/*
* Draw face numbers
*/
cairo_move_to(cr,
thermo->gauge.x - 0.8 * thermo->gauge.radius,
thermo->gauge.y - 0.1 * thermo->gauge.radius);
cairo_show_text(cr, "120");
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);
hexagram_dial_draw_legend(&thermo->dial, cr, 0.75, thermo->dial.max_value, "H");
cairo_set_line_width(cr, 6.0);
incr = (thermo->dial.max_value - thermo->dial.min_value) / 8.0;
for (i=0; i<9; i++) {
double level = thermo->dial.min_value + i * incr;
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);
level += incr;
}
void hexagram_thermo_draw_needle(hexagram_thermo *thermo,
cairo_t *cr,
double temp) {
double fahrenheit = temp * (9.0 / 5.0) + 32.0;
return 0;
if (fahrenheit < 120) {
fahrenheit = 120;
} else if (fahrenheit > 260) {
fahrenheit = 260;
error_dial_draw_bg:
return -1;
}
hexagram_gauge_draw_needle(&thermo->gauge,
cr,
0.9,
(fahrenheit - 120) / 140.0);
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;
}

28
src/trans.c Normal file
View file

@ -0,0 +1,28 @@
#include <stdlib.h>
#include <hexagram/trans.h>
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];
}