]> git.cworth.org Git - acre/blob - acre-x.c
e62c5967601b44b246cd1ff8e1c505332b904c87
[acre] / acre-x.c
1 /* acre - A cairo-based library for creating plots and charts.
2  *
3  * Copyright © 2009 Carl Worth <cworth@cworth.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18  */
19
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <inttypes.h>
25
26 #include <X11/Xlib.h>
27
28 #include <cairo-svg.h>
29 #include <cairo-xlib.h>
30
31 #include "acre.h"
32 #include "math.h"
33
34 #define STRNCMP_LITERAL(var, literal) \
35     strncmp ((var), (literal), sizeof (literal) - 1)
36
37 static acre_t *
38 load_fips_data (const char *filename)
39 {
40         FILE *file;
41         acre_t *acre;
42         acre_data_t *frame_time;
43         char *line = NULL, *s;
44         size_t line_size;
45         ssize_t bytes;
46
47         file = fopen (filename, "r");
48         if (file == NULL) {
49                 fprintf (stderr, "Failed to open %s: %s\n",
50                          filename, strerror (errno));
51                 return NULL;
52         }
53
54         acre = acre_create ();
55
56         acre_set_title (acre, filename);
57         acre_set_x_axis_label (acre, "Frame #");
58         acre_set_y_axis_label (acre, "Frame time (ms)");
59
60         frame_time = acre_data_create ();
61         acre_data_set_style (frame_time, ACRE_STYLE_BARS_OR_LINE);
62
63         while (1) {
64                 int scanned;
65
66                 bytes = getline (&line, &line_size, file);
67                 if (bytes == -1)
68                         break;
69
70                 s = line;
71
72                 if (STRNCMP_LITERAL (s, "frame-time: ") == 0) {
73                         unsigned frame;
74                         int64_t frame_time_ns;
75
76                         s += strlen("frame-time: ");
77
78                         scanned = sscanf (s, "%d %" SCNu64,
79                                           &frame, &frame_time_ns);
80                         if (scanned != 2) {
81                                 fprintf (stderr, "Warning: Failed to parse line: %s\n", line);
82                                 continue;
83                         }
84
85                         acre_data_add_point_2d (frame_time, frame,
86                                                 frame_time_ns / 1e6);
87                 } else {
88                         /* Ignoring all other lines. */
89                 }
90         }
91
92         free (line);
93
94         acre_add_data (acre, frame_time);
95
96         return acre;
97 }
98
99 static void
100 draw (Display *dpy, Window window, Visual *visual, acre_t *acre,
101       int width, int height, double x_min, double x_max)
102 {
103         cairo_t *cr;
104         cairo_surface_t *surface;
105
106         surface = cairo_xlib_surface_create (dpy, window, visual,
107                                              width, height);
108         cr = cairo_create (surface);
109
110         /* Erase to white */
111         cairo_set_source_rgb (cr, 1, 1, 1);
112         cairo_paint (cr);
113
114         acre_set_x_axis_range (acre, x_min, x_max);
115         acre_draw (acre, cr, width, height);
116
117         cairo_destroy (cr);
118
119         cairo_surface_destroy (surface);
120 }
121
122 static void
123 draw_svg (acre_t *acre, int width, int height, double x_min, double x_max)
124 {
125         cairo_t *cr;
126         cairo_surface_t *surface;
127
128         surface = cairo_svg_surface_create ("acre-fips.svg", width, height);
129
130         cr = cairo_create (surface);
131
132         acre_set_x_axis_range (acre, x_min, x_max);
133         acre_draw (acre, cr, width, height);
134
135         cairo_destroy (cr);
136
137         cairo_surface_destroy (surface);
138 }
139
140
141 static void
142 handle_events(Display *dpy, Window window, Visual *visual,
143               acre_t *acre, int width, int height)
144 {
145         XEvent xev;
146         KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q"));
147         KeyCode escape_code = XKeysymToKeycode (dpy, XStringToKeysym("Escape"));
148         KeyCode left_code = XKeysymToKeycode (dpy, XStringToKeysym("Left"));
149         KeyCode right_code = XKeysymToKeycode (dpy, XStringToKeysym("Right"));
150         KeyCode plus_code = XKeysymToKeycode (dpy, XStringToKeysym("plus"));
151         KeyCode equal_code = XKeysymToKeycode (dpy, XStringToKeysym("equal"));
152         KeyCode minus_code = XKeysymToKeycode (dpy, XStringToKeysym("minus"));
153         KeyCode home_code = XKeysymToKeycode (dpy, XStringToKeysym("Home"));
154         KeyCode svg_code = XKeysymToKeycode (dpy, XStringToKeysym("S"));
155         KeyCode keycode;
156         bool need_redraw = false;
157         double x_min, x_max, shift;
158
159         acre_get_x_axis_data_range (acre, &x_min, &x_max);
160
161         while (1) {
162                 if (! XPending (dpy) && need_redraw)
163                         draw (dpy, window, visual, acre,
164                               width, height, x_min, x_max);
165
166 #define PAN  0.05
167 #define ZOOM PAN
168                 XNextEvent (dpy, &xev);
169                 switch (xev.type) {
170                 case KeyPress:
171                         need_redraw = true;
172                         keycode = xev.xkey.keycode;
173                         if (keycode == quit_code ||
174                             keycode == escape_code)
175                         {
176                                 return;
177                         }
178                         else if (keycode == left_code)
179                         {
180                                 shift = PAN * (x_max - x_min);
181                                 x_min += shift;
182                                 x_max += shift;
183                         }
184                         else if (keycode == right_code)
185                         {
186                                 shift = PAN * (x_max - x_min);
187                                 x_min -= shift;
188                                 x_max -= shift;
189                         }
190                         else if (keycode == plus_code ||
191                                    keycode == equal_code)
192                         {
193                                 shift = ZOOM * (x_max - x_min);
194                                 x_min += shift;
195                                 x_max -= shift;
196                         }
197                         else if (keycode == minus_code)
198                         {
199                                 shift = (ZOOM/(1 - 2 * ZOOM)) * (x_max - x_min);
200                                 x_min -= shift;
201                                 x_max += shift;
202                         }
203                         else if (keycode == home_code)
204                         {
205                                 acre_get_x_axis_data_range (acre, &x_min, &x_max);
206                         }
207                         else if (keycode == svg_code)
208                         {
209                                 need_redraw = false;
210                                 draw_svg (acre, width, height, x_min, x_max);
211                         }
212                         else
213                         {
214                                 need_redraw = false;
215                         }
216                         break;
217                 case ConfigureNotify:
218                         width = xev.xconfigure.width;
219                         height = xev.xconfigure.height;
220                         break;
221                 case Expose:
222                         if (xev.xexpose.count == 0)
223                                 need_redraw = 1;
224                         break;
225                 }
226         }
227 }
228
229 int
230 main (int argc, char *argv[])
231 {
232         Display *dpy;
233         Window window, root;
234         Visual *visual;
235         acre_t *acre;
236         XSetWindowAttributes window_attr;
237         Colormap colormap;
238         unsigned long window_mask, event_mask;
239         unsigned long white;
240
241         int width = 800;
242         int height = 600;
243
244         if (argc < 2) {
245                 fprintf (stderr, "Usage: acre-x data-file\n");
246                 fprintf (stderr, "Where the data file is the output from fips (with frame-timing feature.\n");
247                 exit (1);
248         }
249
250         acre = load_fips_data (argv[1]);
251
252         if (acre == NULL)
253                 return 1;
254
255         dpy = XOpenDisplay (NULL);
256
257         if (dpy == NULL) {
258                 fprintf(stderr, "Failed to open display %s\n",
259                         XDisplayName(NULL));
260                 return 1;
261         }
262
263         root = DefaultRootWindow (dpy);
264         white = WhitePixel (dpy, DefaultScreen (dpy));
265         visual = DefaultVisual (dpy, DefaultScreen (dpy));
266         colormap = XCreateColormap (dpy, root, visual, AllocNone);
267         event_mask = KeyPressMask | StructureNotifyMask | ExposureMask;
268
269         window_mask = 0;
270         window_mask |= CWBackPixel;     window_attr.background_pixel = white;
271         window_mask |= CWBorderPixel;   window_attr.border_pixel = white;
272         window_mask |= CWColormap;      window_attr.colormap = colormap;
273         window_mask |= CWEventMask;     window_attr.event_mask = event_mask;
274
275         window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
276                                DefaultDepth (dpy, DefaultScreen (dpy)),
277                                InputOutput, visual,
278                                window_mask, &window_attr);
279
280         XMapWindow (dpy, window);
281
282         handle_events (dpy, window, visual, acre, width, height);
283
284         acre_destroy (acre);
285
286         XDestroyWindow (dpy, window);
287         XCloseDisplay (dpy);
288
289         return 0;
290 }