]> git.cworth.org Git - acre/blob - acre-x.c
Add interactive zooming and panning.
[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 <stdbool.h>
21
22 #include <X11/Xlib.h>
23 #include <cairo-xlib.h>
24
25 #include "acre.h"
26 #include "math.h"
27
28 static acre_t *
29 load_chart (void)
30 {
31         acre_t *acre;
32         acre_data_t *data0, *data1, *data2;
33         int i;
34
35         acre = acre_create ();
36         acre_set_x_axis_label (acre, "X axis");
37         acre_set_y_axis_label (acre, "Y axis");
38
39         data0 = acre_data_create ();
40         data1 = acre_data_create ();
41         data2 = acre_data_create ();
42
43         acre_data_set_name (data0, "Data 0");
44         acre_data_set_name (data1, "Data 1");
45         acre_data_set_name (data2, "Data 2");
46
47         for (i = 0; i <= 100; i++) {
48                 acre_data_add_point_2d (data0, i,   0 - (i/3.0)*(i/3.0));
49         }
50
51         for (i = 0; i < 100; i++) {
52                 double t = 1.0 - (i / 100.0);
53                 acre_data_add_point_2d (data1, i, -1000 * (1.0 - t*t*t));
54         }
55         
56         for (i = 0; i <= 1000; i++) {
57                 double t, x, y;
58                 t = i/10.0 - 50;
59                 x = t + 50;
60                 if (t == 0.0)
61                         y = -200;
62                 else
63                         y = -1200 + 1000 * sin(t) / t;
64                 acre_data_add_point_2d (data2, x, y);
65         }
66
67         acre_add_data (acre, data0);
68         acre_add_data (acre, data1);
69         acre_add_data (acre, data2);
70
71         acre_set_title (acre, "All the data");
72
73         return acre;
74 }
75
76 static void
77 draw (Display *dpy, Window window, Visual *visual, acre_t *acre,
78       int width, int height, double x_min, double x_max)
79 {
80         cairo_t *cr;
81         cairo_surface_t *surface;
82
83         surface = cairo_xlib_surface_create (dpy, window, visual,
84                                              width, height);
85         cr = cairo_create (surface);
86
87         /* Erase to white */
88         cairo_set_source_rgb (cr, 1, 1, 1);
89         cairo_paint (cr);
90
91         acre_set_x_axis_range (acre, x_min, x_max);
92         acre_draw (acre, cr, width, height);
93
94         cairo_destroy (cr);
95
96         cairo_surface_destroy (surface);
97 }
98
99
100 static void
101 handle_events(Display *dpy, Window window, Visual *visual,
102               acre_t *acre, int width, int height)
103 {
104         XEvent xev;
105         KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q"));
106         KeyCode left_code = XKeysymToKeycode (dpy, XStringToKeysym("Left"));
107         KeyCode right_code = XKeysymToKeycode (dpy, XStringToKeysym("Right"));
108         KeyCode plus_code = XKeysymToKeycode (dpy, XStringToKeysym("plus"));
109         KeyCode equal_code = XKeysymToKeycode (dpy, XStringToKeysym("equal"));
110         KeyCode minus_code = XKeysymToKeycode (dpy, XStringToKeysym("minus"));
111         KeyCode home_code = XKeysymToKeycode (dpy, XStringToKeysym("Home"));
112         KeyCode keycode;
113         bool need_redraw = false;
114         double x_min, x_max, shift;
115
116         acre_get_x_axis_data_range (acre, &x_min, &x_max);
117
118         while (1) {
119                 if (! XPending (dpy) && need_redraw)
120                         draw (dpy, window, visual, acre,
121                               width, height, x_min, x_max);
122
123                 XNextEvent (dpy, &xev);
124                 switch (xev.type) {
125                 case KeyPress:
126                         keycode = xev.xkey.keycode;
127                         if (keycode == quit_code)
128                         {
129                                 return;
130                         }
131                         else if (keycode == left_code)
132                         {
133                                 shift = 0.25 * (x_max - x_min);
134                                 x_min += shift;
135                                 x_max += shift;
136                         }
137                         else if (keycode == right_code)
138                         {
139                                 shift = 0.25 * (x_max - x_min);
140                                 x_min -= shift;
141                                 x_max -= shift;
142                         }
143                         else if (keycode == plus_code ||
144                                    keycode == equal_code)
145                         {
146                                 shift = 0.25 * (x_max - x_min);
147                                 x_min += shift;
148                                 x_max -= shift;
149                         }
150                         else if (keycode == minus_code)
151                         {
152                                 shift = 0.5 * (x_max - x_min);
153                                 x_min -= shift;
154                                 x_max += shift;
155                         }
156                         else if (keycode == home_code)
157                         {
158                                 acre_get_x_axis_data_range (acre, &x_min, &x_max);
159                         }
160                         need_redraw = 1;
161                         break;
162                 case ConfigureNotify:
163                         width = xev.xconfigure.width;
164                         height = xev.xconfigure.height;
165                         break;
166                 case Expose:
167                         if (xev.xexpose.count == 0)
168                                 need_redraw = 1;
169                         break;
170                 }
171         }
172 }
173
174 int
175 main (void)
176 {
177         Display *dpy;
178         Window window, root;
179         Visual *visual;
180         acre_t *acre;
181         XSetWindowAttributes window_attr;
182         Colormap colormap;
183         unsigned long window_mask, event_mask;
184         unsigned long white;
185
186         int width = 800;
187         int height = 600;
188
189         acre = load_chart ();
190
191         dpy = XOpenDisplay (NULL);
192
193         if (dpy == NULL) {
194                 fprintf(stderr, "Failed to open display %s\n",
195                         XDisplayName(NULL));
196                 return 1;
197         }
198
199         root = DefaultRootWindow (dpy);
200         white = WhitePixel (dpy, DefaultScreen (dpy));
201         visual = DefaultVisual (dpy, DefaultScreen (dpy));
202         colormap = XCreateColormap (dpy, root, visual, AllocNone);
203         event_mask = KeyPressMask | StructureNotifyMask | ExposureMask;
204
205         window_mask = 0;
206         window_mask |= CWBackPixel;     window_attr.background_pixel = white;
207         window_mask |= CWBorderPixel;   window_attr.border_pixel = white;
208         window_mask |= CWColormap;      window_attr.colormap = colormap;
209         window_mask |= CWEventMask;     window_attr.event_mask = event_mask;
210
211         window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
212                                DefaultDepth (dpy, DefaultScreen (dpy)),
213                                InputOutput, visual,
214                                window_mask, &window_attr);
215
216         XMapWindow (dpy, window);
217
218         handle_events (dpy, window, visual, acre, width, height);
219
220         acre_destroy (acre);
221
222         XDestroyWindow (dpy, window);
223         XCloseDisplay (dpy);
224
225         return 0;
226 }