]> git.cworth.org Git - acre/blob - acre-x.c
Draw frame times as a bar chart.
[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 #include <cairo-xlib.h>
28
29 #include "acre.h"
30 #include "math.h"
31
32 #define STRNCMP_LITERAL(var, literal) \
33     strncmp ((var), (literal), sizeof (literal) - 1)
34
35 static acre_t *
36 load_fips_data (const char *filename)
37 {
38         FILE *file;
39         acre_t *acre;
40         acre_data_t *frame_time;
41         char *line = NULL, *s;
42         size_t line_size;
43         ssize_t bytes;
44
45         file = fopen (filename, "r");
46         if (file == NULL) {
47                 fprintf (stderr, "Failed to open %s: %s\n",
48                          filename, strerror (errno));
49                 return NULL;
50         }
51
52         acre = acre_create ();
53
54         acre_set_title (acre, filename);
55         acre_set_x_axis_label (acre, "Frame #");
56         acre_set_y_axis_label (acre, "Frame time (ms)");
57
58         frame_time = acre_data_create ();
59         acre_data_set_style (frame_time, ACRE_STYLE_BARS_OR_LINE);
60
61         while (1) {
62                 int scanned;
63
64                 bytes = getline (&line, &line_size, file);
65                 if (bytes == -1)
66                         break;
67
68                 s = line;
69
70                 if (STRNCMP_LITERAL (s, "frame-time: ") == 0) {
71                         unsigned frame;
72                         int64_t frame_time_ns;
73
74                         s += strlen("frame-time: ");
75
76                         scanned = sscanf (s, "%d %" SCNu64,
77                                           &frame, &frame_time_ns);
78                         if (scanned != 2) {
79                                 fprintf (stderr, "Warning: Failed to parse line: %s\n", line);
80                                 continue;
81                         }
82
83                         acre_data_add_point_2d (frame_time, frame,
84                                                 frame_time_ns / 1e6);
85                 } else {
86                         /* Ignoring all other lines. */
87                 }
88         }
89
90         free (line);
91
92         acre_add_data (acre, frame_time);
93
94         return acre;
95 }
96
97 static void
98 draw (Display *dpy, Window window, Visual *visual, acre_t *acre,
99       int width, int height, double x_min, double x_max)
100 {
101         cairo_t *cr;
102         cairo_surface_t *surface;
103
104         surface = cairo_xlib_surface_create (dpy, window, visual,
105                                              width, height);
106         cr = cairo_create (surface);
107
108         /* Erase to white */
109         cairo_set_source_rgb (cr, 1, 1, 1);
110         cairo_paint (cr);
111
112         acre_set_x_axis_range (acre, x_min, x_max);
113         acre_draw (acre, cr, width, height);
114
115         cairo_destroy (cr);
116
117         cairo_surface_destroy (surface);
118 }
119
120
121 static void
122 handle_events(Display *dpy, Window window, Visual *visual,
123               acre_t *acre, int width, int height)
124 {
125         XEvent xev;
126         KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q"));
127         KeyCode escape_code = XKeysymToKeycode (dpy, XStringToKeysym("Escape"));
128         KeyCode left_code = XKeysymToKeycode (dpy, XStringToKeysym("Left"));
129         KeyCode right_code = XKeysymToKeycode (dpy, XStringToKeysym("Right"));
130         KeyCode plus_code = XKeysymToKeycode (dpy, XStringToKeysym("plus"));
131         KeyCode equal_code = XKeysymToKeycode (dpy, XStringToKeysym("equal"));
132         KeyCode minus_code = XKeysymToKeycode (dpy, XStringToKeysym("minus"));
133         KeyCode home_code = XKeysymToKeycode (dpy, XStringToKeysym("Home"));
134         KeyCode keycode;
135         bool need_redraw = false;
136         double x_min, x_max, shift;
137
138         acre_get_x_axis_data_range (acre, &x_min, &x_max);
139
140         while (1) {
141                 if (! XPending (dpy) && need_redraw)
142                         draw (dpy, window, visual, acre,
143                               width, height, x_min, x_max);
144
145 #define PAN  0.05
146 #define ZOOM PAN
147                 XNextEvent (dpy, &xev);
148                 switch (xev.type) {
149                 case KeyPress:
150                         need_redraw = true;
151                         keycode = xev.xkey.keycode;
152                         if (keycode == quit_code ||
153                             keycode == escape_code)
154                         {
155                                 return;
156                         }
157                         else if (keycode == left_code)
158                         {
159                                 shift = PAN * (x_max - x_min);
160                                 x_min += shift;
161                                 x_max += shift;
162                         }
163                         else if (keycode == right_code)
164                         {
165                                 shift = PAN * (x_max - x_min);
166                                 x_min -= shift;
167                                 x_max -= shift;
168                         }
169                         else if (keycode == plus_code ||
170                                    keycode == equal_code)
171                         {
172                                 shift = ZOOM * (x_max - x_min);
173                                 x_min += shift;
174                                 x_max -= shift;
175                         }
176                         else if (keycode == minus_code)
177                         {
178                                 shift = (ZOOM/(1 - 2 * ZOOM)) * (x_max - x_min);
179                                 x_min -= shift;
180                                 x_max += shift;
181                         }
182                         else if (keycode == home_code)
183                         {
184                                 acre_get_x_axis_data_range (acre, &x_min, &x_max);
185                         }
186                         else
187                         {
188                                 need_redraw = false;
189                         }
190                         break;
191                 case ConfigureNotify:
192                         width = xev.xconfigure.width;
193                         height = xev.xconfigure.height;
194                         break;
195                 case Expose:
196                         if (xev.xexpose.count == 0)
197                                 need_redraw = 1;
198                         break;
199                 }
200         }
201 }
202
203 int
204 main (int argc, char *argv[])
205 {
206         Display *dpy;
207         Window window, root;
208         Visual *visual;
209         acre_t *acre;
210         XSetWindowAttributes window_attr;
211         Colormap colormap;
212         unsigned long window_mask, event_mask;
213         unsigned long white;
214
215         int width = 800;
216         int height = 600;
217
218         if (argc < 2) {
219                 fprintf (stderr, "Usage: acre-x data-file\n");
220                 fprintf (stderr, "Where the data file is the output from fips (with frame-timing feature.\n");
221                 exit (1);
222         }
223
224         acre = load_fips_data (argv[1]);
225
226         if (acre == NULL)
227                 return 1;
228
229         dpy = XOpenDisplay (NULL);
230
231         if (dpy == NULL) {
232                 fprintf(stderr, "Failed to open display %s\n",
233                         XDisplayName(NULL));
234                 return 1;
235         }
236
237         root = DefaultRootWindow (dpy);
238         white = WhitePixel (dpy, DefaultScreen (dpy));
239         visual = DefaultVisual (dpy, DefaultScreen (dpy));
240         colormap = XCreateColormap (dpy, root, visual, AllocNone);
241         event_mask = KeyPressMask | StructureNotifyMask | ExposureMask;
242
243         window_mask = 0;
244         window_mask |= CWBackPixel;     window_attr.background_pixel = white;
245         window_mask |= CWBorderPixel;   window_attr.border_pixel = white;
246         window_mask |= CWColormap;      window_attr.colormap = colormap;
247         window_mask |= CWEventMask;     window_attr.event_mask = event_mask;
248
249         window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
250                                DefaultDepth (dpy, DefaultScreen (dpy)),
251                                InputOutput, visual,
252                                window_mask, &window_attr);
253
254         XMapWindow (dpy, window);
255
256         handle_events (dpy, window, visual, acre, width, height);
257
258         acre_destroy (acre);
259
260         XDestroyWindow (dpy, window);
261         XCloseDisplay (dpy);
262
263         return 0;
264 }