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