123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- #include "TimerGraph.h"
- #include <unistd.h>
- #include <sys/wait.h>
- struct _TimerGraph {
- GtkBin parent;
- GtkWidget *drawingArea;
- GdkPixbuf *originalData;
- cairo_surface_t *tex;
- double gx;
- double gy;
- };
- G_DEFINE_TYPE(TimerGraph, timer_graph, GTK_TYPE_BIN);
- static char *get_graph_format() {
- GSList *list = gdk_pixbuf_get_formats();
- GSList *i;
- for (i = list; i != NULL; i = i->next) {
- GdkPixbufFormat *f = i->data;
- if (!gdk_pixbuf_format_is_disabled(f)) {
- gchar **exts = gdk_pixbuf_format_get_extensions(f);
- while (*exts != NULL) {
- if (strcmp(*exts, PLOTUTILS_PREFERRED_FORMAT) == 0){
- g_slist_free(list);
- return PLOTUTILS_PREFERRED_FORMAT;
- }
- ++exts;
- }
- }
- }
- g_warning("fallback image forat ('" PLOTUTILS_FALLBACK_FORMAT "') used");
- g_slist_free(list);
- return PLOTUTILS_FALLBACK_FORMAT;
- }
- static char *create_graph_bytes(const TimerGraphPoint *verts, gsize length, const char *format, const char *xLabel, const char *yLabel, const char *title, gsize *len) {
- GString *dataString = g_string_new(NULL);
- gsize i;
- for (i = 0; i < length; ++i) {
- g_string_append_printf(dataString, "%d.0 %d.0", verts[i].x, verts[i].y);
- if (i < length - 1) {
- g_string_append_c(dataString, '\n');
- }
- }
- char *pointsString = g_string_free(dataString, FALSE);
- int ptoc[2];
- if (pipe(ptoc) < 0) {
- g_free(pointsString);
- GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- return NULL;
- }
- int ctop[2];
- if (pipe(ctop) < 0) {
- g_free(pointsString);
- close(ptoc[0]);
- close(ptoc[1]);
- GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- return NULL;
- }
- pid_t p = fork();
- if (p == 0) {
- /* child */
- g_free(pointsString);
- dup2(ptoc[0], fileno(stdin));
- close(ptoc[1]);
- dup2(ctop[1], fileno(stdout));
- close(ctop[0]);
- freopen("/dev/null", "w", stderr);
- execlp(PLOTUTILS_GRAPH_PATH, PLOTUTILS_GRAPH_PATH, "-T", format, "-X", xLabel, "-Y", yLabel, "-L", title, NULL);
- /* If graph(1) could not be executed */
- exit(0);
- } else {
- /* parent */
- int out = ptoc[1];
- close(ptoc[0]);
- int in = ctop[0];
- close(ctop[1]);
- write(out, pointsString, strlen(pointsString));
- close(out);
- g_free(pointsString);
- int status;
- if (waitpid(p, &status, 0) < 0) {
- close(in);
- g_warning("graph(1) failed");
- return NULL;
- } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
- close(in);
- g_warning("graph(1) failed");
- return NULL;
- }
- GString *buff = g_string_new(NULL);
- char c;
- *len = 0;
- while (read(in, &c, 1) > 0) {
- g_string_append_c(buff, c);
- ++(*len);
- }
- close(in);
- char *resp = g_string_free(buff, FALSE);
- return resp;
- }
- }
- static GdkPixbuf *pixbuf_from_data(const char *data, gsize len) {
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- GError *err = NULL;
- gdk_pixbuf_loader_write(loader, (const guchar *) data, len, &err);
- gdk_pixbuf_loader_close(loader, NULL);
- if (err != NULL) {
- GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- g_object_unref(loader);
- g_error_free(err);
- return NULL;
- }
- GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
- g_object_ref(pixbuf);
- g_object_unref(loader);
- return pixbuf;
- }
- static void da_size_callback(GtkDrawingArea *self, GtkAllocation *allocation, TimerGraph *graph) {
- if (graph->originalData) {
- int size = MIN(allocation->width, allocation->height);
- if (graph->tex) {
- cairo_surface_destroy(graph->tex);
- }
- graph->gx = ((double) allocation->width / 2) - ((double) size / 2);
- graph->gy = ((double) allocation->height / 2) - ((double) size / 2);
- GdkPixbuf *scaled = gdk_pixbuf_scale_simple(graph->originalData, size, size, GDK_INTERP_BILINEAR);
- graph->tex = gdk_cairo_surface_create_from_pixbuf(scaled, 1, gtk_widget_get_window(GTK_WIDGET(self)));
- g_object_unref(scaled);
- }
- }
- static gboolean da_draw_callback(GtkDrawingArea *self, cairo_t *cr, TimerGraph *graph) {
- GtkStyleContext *cxt = gtk_widget_get_style_context(GTK_WIDGET(self));
- guint width = gtk_widget_get_allocated_width(GTK_WIDGET(self));
- guint height = gtk_widget_get_allocated_height(GTK_WIDGET(self));
- gtk_render_background(cxt, cr, 0, 0, width, height);
- if (graph->tex) {
- cairo_set_source_surface(cr, graph->tex, graph->gx, graph->gy);
- }
- cairo_paint(cr);
- return TRUE;
- }
- GtkWidget *timer_graph_new(const TimerGraphPoint *verts, gsize length, const char *xLabel, const char *yLabel, const char *title) {
- TimerGraph *g = g_object_new(TIMER_TYPE_GRAPH, NULL);
- gsize len = 0;
- char *data = create_graph_bytes(verts, length, get_graph_format(), xLabel, yLabel, title, &len);
- g->originalData = pixbuf_from_data(data, len);
- g_free(data);
- return GTK_WIDGET(g);
- }
- static void timer_graph_finalize(GObject *obj) {
- TimerGraph *self = TIMER_GRAPH(obj);
- if (self->originalData) {
- g_object_unref(self->originalData);
- }
- if (self->tex) {
- cairo_surface_destroy(self->tex);
- }
- G_OBJECT_CLASS(timer_graph_parent_class)->finalize(obj);
- }
- static void timer_graph_class_init(TimerGraphClass *class) {
- G_OBJECT_CLASS(class)->finalize = timer_graph_finalize;
- }
- static void timer_graph_init(TimerGraph* self) {
- self->drawingArea = gtk_drawing_area_new();
- gtk_container_add(GTK_CONTAINER(self), self->drawingArea);
- gtk_widget_set_size_request(GTK_WIDGET(self), 100, 100);
- g_signal_connect(self->drawingArea, "size-allocate", G_CALLBACK(da_size_callback), self);
- g_signal_connect(self->drawingArea, "draw", G_CALLBACK(da_draw_callback), self);
- }
|