TimerGraph.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #include "TimerGraph.h"
  2. #include <unistd.h>
  3. #include <sys/wait.h>
  4. struct _TimerGraph {
  5. GtkBin parent;
  6. GtkWidget *drawingArea;
  7. GdkPixbuf *originalData;
  8. cairo_surface_t *tex;
  9. double gx;
  10. double gy;
  11. };
  12. G_DEFINE_TYPE(TimerGraph, timer_graph, GTK_TYPE_BIN);
  13. static char *get_graph_format() {
  14. GSList *list = gdk_pixbuf_get_formats();
  15. GSList *i;
  16. for (i = list; i != NULL; i = i->next) {
  17. GdkPixbufFormat *f = i->data;
  18. if (!gdk_pixbuf_format_is_disabled(f)) {
  19. gchar **exts = gdk_pixbuf_format_get_extensions(f);
  20. while (*exts != NULL) {
  21. if (strcmp(*exts, PLOTUTILS_PREFERRED_FORMAT) == 0){
  22. g_slist_free(list);
  23. return PLOTUTILS_PREFERRED_FORMAT;
  24. }
  25. ++exts;
  26. }
  27. }
  28. }
  29. g_warning("fallback image forat ('" PLOTUTILS_FALLBACK_FORMAT "') used");
  30. g_slist_free(list);
  31. return PLOTUTILS_FALLBACK_FORMAT;
  32. }
  33. static char *create_graph_bytes(const TimerGraphPoint *verts, gsize length, const char *format, const char *xLabel, const char *yLabel, const char *title, gsize *len) {
  34. GString *dataString = g_string_new(NULL);
  35. gsize i;
  36. for (i = 0; i < length; ++i) {
  37. g_string_append_printf(dataString, "%d.0 %d.0", verts[i].x, verts[i].y);
  38. if (i < length - 1) {
  39. g_string_append_c(dataString, '\n');
  40. }
  41. }
  42. char *pointsString = g_string_free(dataString, FALSE);
  43. int ptoc[2];
  44. if (pipe(ptoc) < 0) {
  45. g_free(pointsString);
  46. GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
  47. gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
  48. gtk_dialog_run(GTK_DIALOG(diag));
  49. gtk_widget_destroy(diag);
  50. return NULL;
  51. }
  52. int ctop[2];
  53. if (pipe(ctop) < 0) {
  54. g_free(pointsString);
  55. close(ptoc[0]);
  56. close(ptoc[1]);
  57. GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
  58. gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
  59. gtk_dialog_run(GTK_DIALOG(diag));
  60. gtk_widget_destroy(diag);
  61. return NULL;
  62. }
  63. pid_t p = fork();
  64. if (p == 0) {
  65. /* child */
  66. g_free(pointsString);
  67. dup2(ptoc[0], fileno(stdin));
  68. close(ptoc[1]);
  69. dup2(ctop[1], fileno(stdout));
  70. close(ctop[0]);
  71. freopen("/dev/null", "w", stderr);
  72. execlp(PLOTUTILS_GRAPH_PATH, PLOTUTILS_GRAPH_PATH, "-T", format, "-X", xLabel, "-Y", yLabel, "-L", title, NULL);
  73. /* If graph(1) could not be executed */
  74. exit(0);
  75. } else {
  76. /* parent */
  77. int out = ptoc[1];
  78. close(ptoc[0]);
  79. int in = ctop[0];
  80. close(ctop[1]);
  81. write(out, pointsString, strlen(pointsString));
  82. close(out);
  83. g_free(pointsString);
  84. int status;
  85. if (waitpid(p, &status, 0) < 0) {
  86. close(in);
  87. g_warning("graph(1) failed");
  88. return NULL;
  89. } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
  90. close(in);
  91. g_warning("graph(1) failed");
  92. return NULL;
  93. }
  94. GString *buff = g_string_new(NULL);
  95. char c;
  96. *len = 0;
  97. while (read(in, &c, 1) > 0) {
  98. g_string_append_c(buff, c);
  99. ++(*len);
  100. }
  101. close(in);
  102. char *resp = g_string_free(buff, FALSE);
  103. return resp;
  104. }
  105. }
  106. static GdkPixbuf *pixbuf_from_data(const char *data, gsize len) {
  107. GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
  108. GError *err = NULL;
  109. gdk_pixbuf_loader_write(loader, (const guchar *) data, len, &err);
  110. gdk_pixbuf_loader_close(loader, NULL);
  111. if (err != NULL) {
  112. GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "An internal error occured. Please try again.");
  113. gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
  114. gtk_dialog_run(GTK_DIALOG(diag));
  115. gtk_widget_destroy(diag);
  116. g_object_unref(loader);
  117. g_error_free(err);
  118. return NULL;
  119. }
  120. GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
  121. g_object_ref(pixbuf);
  122. g_object_unref(loader);
  123. return pixbuf;
  124. }
  125. static void da_size_callback(GtkDrawingArea *self, GtkAllocation *allocation, TimerGraph *graph) {
  126. if (graph->originalData) {
  127. int size = MIN(allocation->width, allocation->height);
  128. if (graph->tex) {
  129. cairo_surface_destroy(graph->tex);
  130. }
  131. graph->gx = ((double) allocation->width / 2) - ((double) size / 2);
  132. graph->gy = ((double) allocation->height / 2) - ((double) size / 2);
  133. GdkPixbuf *scaled = gdk_pixbuf_scale_simple(graph->originalData, size, size, GDK_INTERP_BILINEAR);
  134. graph->tex = gdk_cairo_surface_create_from_pixbuf(scaled, 1, gtk_widget_get_window(GTK_WIDGET(self)));
  135. g_object_unref(scaled);
  136. }
  137. }
  138. static gboolean da_draw_callback(GtkDrawingArea *self, cairo_t *cr, TimerGraph *graph) {
  139. GtkStyleContext *cxt = gtk_widget_get_style_context(GTK_WIDGET(self));
  140. guint width = gtk_widget_get_allocated_width(GTK_WIDGET(self));
  141. guint height = gtk_widget_get_allocated_height(GTK_WIDGET(self));
  142. gtk_render_background(cxt, cr, 0, 0, width, height);
  143. if (graph->tex) {
  144. cairo_set_source_surface(cr, graph->tex, graph->gx, graph->gy);
  145. }
  146. cairo_paint(cr);
  147. return TRUE;
  148. }
  149. GtkWidget *timer_graph_new(const TimerGraphPoint *verts, gsize length, const char *xLabel, const char *yLabel, const char *title) {
  150. TimerGraph *g = g_object_new(TIMER_TYPE_GRAPH, NULL);
  151. gsize len = 0;
  152. char *data = create_graph_bytes(verts, length, get_graph_format(), xLabel, yLabel, title, &len);
  153. g->originalData = pixbuf_from_data(data, len);
  154. g_free(data);
  155. return GTK_WIDGET(g);
  156. }
  157. static void timer_graph_finalize(GObject *obj) {
  158. TimerGraph *self = TIMER_GRAPH(obj);
  159. if (self->originalData) {
  160. g_object_unref(self->originalData);
  161. }
  162. if (self->tex) {
  163. cairo_surface_destroy(self->tex);
  164. }
  165. G_OBJECT_CLASS(timer_graph_parent_class)->finalize(obj);
  166. }
  167. static void timer_graph_class_init(TimerGraphClass *class) {
  168. G_OBJECT_CLASS(class)->finalize = timer_graph_finalize;
  169. }
  170. static void timer_graph_init(TimerGraph* self) {
  171. self->drawingArea = gtk_drawing_area_new();
  172. gtk_container_add(GTK_CONTAINER(self), self->drawingArea);
  173. gtk_widget_set_size_request(GTK_WIDGET(self), 100, 100);
  174. g_signal_connect(self->drawingArea, "size-allocate", G_CALLBACK(da_size_callback), self);
  175. g_signal_connect(self->drawingArea, "draw", G_CALLBACK(da_draw_callback), self);
  176. }