Tizen 2.0 Release
[framework/graphics/cairo.git] / util / cairo-fdr / fdr.c
1 /* cairo-fdr - a 'flight data recorder', a black box, for cairo
2  *
3  * Copyright © 2009 Chris Wilson
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #define _GNU_SOURCE
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <cairo.h>
26 #include <cairo-script.h>
27 #include <cairo-tee.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <signal.h>
31
32 #include <dlfcn.h>
33
34 static void *_dlhandle = RTLD_NEXT;
35 #define DLCALL(name, args...) ({ \
36     static typeof (&name) name##_real; \
37     if (name##_real == NULL) { \
38         name##_real = dlsym (_dlhandle, #name); \
39         if (name##_real == NULL && _dlhandle == RTLD_NEXT) { \
40             _dlhandle = dlopen ("libcairo.so", RTLD_LAZY); \
41             name##_real = dlsym (_dlhandle, #name); \
42             assert (name##_real != NULL); \
43         } \
44     } \
45     (*name##_real) (args);  \
46 })
47
48 #define RINGBUFFER_SIZE 16
49 static cairo_surface_t *fdr_ringbuffer[RINGBUFFER_SIZE];
50 static int fdr_position;
51 static int fdr_dump;
52
53 static const cairo_user_data_key_t fdr_key;
54
55 static void
56 fdr_replay_to_script (cairo_surface_t *recording, cairo_device_t *ctx)
57 {
58     if (recording != NULL) {
59         DLCALL (cairo_script_write_comment, ctx, "--- fdr ---", -1);
60         DLCALL (cairo_script_from_recording_surface, ctx, recording);
61     }
62 }
63
64 static void
65 fdr_dump_ringbuffer (void)
66 {
67     cairo_device_t *ctx;
68     int n;
69
70     ctx = DLCALL (cairo_script_create, "/tmp/fdr.trace");
71
72     for (n = fdr_position; n < RINGBUFFER_SIZE; n++)
73         fdr_replay_to_script (fdr_ringbuffer[n], ctx);
74
75     for (n = 0; n < fdr_position; n++)
76         fdr_replay_to_script (fdr_ringbuffer[n], ctx);
77
78     DLCALL (cairo_device_destroy, ctx);
79 }
80
81 static void
82 fdr_sighandler (int sig)
83 {
84     fdr_dump = 1;
85 }
86
87 static void
88 fdr_urgent_sighandler (int sig)
89 {
90     fdr_dump_ringbuffer ();
91 }
92
93 static void
94 fdr_atexit (void)
95 {
96     if (fdr_dump)
97         fdr_dump_ringbuffer ();
98 }
99
100 static void
101 fdr_pending_signals (void)
102 {
103     static int initialized;
104
105     if (! initialized) {
106         initialized = 1;
107
108         signal (SIGUSR1, fdr_sighandler);
109
110         signal (SIGSEGV, fdr_urgent_sighandler);
111         signal (SIGABRT, fdr_urgent_sighandler);
112         atexit (fdr_atexit);
113     }
114
115     if (fdr_dump) {
116         fdr_dump_ringbuffer ();
117         fdr_dump = 0;
118     }
119 }
120
121 static void
122 fdr_get_extents (cairo_surface_t *surface,
123                  cairo_rectangle_t *extents)
124 {
125     cairo_t *cr;
126
127     cr = DLCALL (cairo_create, surface);
128     DLCALL (cairo_clip_extents, cr,
129             &extents->x, &extents->y, &extents->width, &extents->height);
130     DLCALL (cairo_destroy, cr);
131
132     extents->width -= extents->x;
133     extents->height -= extents->y;
134 }
135
136 static void
137 fdr_surface_destroy (void *surface)
138 {
139     DLCALL (cairo_surface_destroy, surface);
140 }
141
142 static void
143 fdr_surface_reference (void *surface)
144 {
145     DLCALL (cairo_surface_reference, surface);
146 }
147
148 static cairo_surface_t *
149 fdr_surface_get_tee (cairo_surface_t *surface)
150 {
151     return DLCALL (cairo_surface_get_user_data, surface, &fdr_key);
152 }
153
154 static cairo_surface_t *
155 fdr_tee_surface_index (cairo_surface_t *surface, int index)
156 {
157     return DLCALL (cairo_tee_surface_index, surface, index);
158 }
159
160 cairo_t *
161 cairo_create (cairo_surface_t *surface)
162 {
163     cairo_surface_t *record, *tee;
164
165     fdr_pending_signals ();
166
167     tee = fdr_surface_get_tee (surface);
168     if (tee == NULL) {
169         cairo_rectangle_t extents;
170         cairo_content_t content;
171
172         fdr_get_extents (surface, &extents);
173         content = DLCALL (cairo_surface_get_content, surface);
174
175         tee = DLCALL (cairo_tee_surface_create, surface);
176         record = DLCALL (cairo_recording_surface_create, content, &extents);
177         DLCALL (cairo_tee_surface_add, tee, record);
178
179         DLCALL (cairo_surface_set_user_data, surface,
180                 &fdr_key, tee, fdr_surface_destroy);
181     } else {
182         int n;
183
184         record = fdr_tee_surface_index (tee, 1);
185
186         /* update the position of the recording surface in the ringbuffer */
187         for (n = 0; n < RINGBUFFER_SIZE; n++) {
188             if (record == fdr_ringbuffer[n]) {
189                 fdr_ringbuffer[n] = NULL;
190                 break;
191             }
192         }
193     }
194
195     fdr_surface_destroy (fdr_ringbuffer[fdr_position]);
196     fdr_ringbuffer[fdr_position] = record;
197     fdr_position = (fdr_position + 1) % RINGBUFFER_SIZE;
198
199     return DLCALL (cairo_create, tee);
200 }
201
202 static void
203 fdr_remove_tee (cairo_surface_t *surface)
204 {
205     fdr_surface_reference (surface);
206     DLCALL (cairo_surface_set_user_data, surface, &fdr_key, NULL, NULL);
207     fdr_surface_destroy (surface);
208 }
209
210 void
211 cairo_destroy (cairo_t *cr)
212 {
213     cairo_surface_t *tee;
214
215     tee = DLCALL (cairo_get_target, cr);
216     DLCALL (cairo_destroy, cr);
217
218     if (DLCALL (cairo_surface_get_reference_count, tee) == 1)
219         fdr_remove_tee (fdr_tee_surface_index (tee, 0));
220 }
221
222 void
223 cairo_pattern_destroy (cairo_pattern_t *pattern)
224 {
225     if (DLCALL (cairo_pattern_get_type, pattern) == CAIRO_PATTERN_TYPE_SURFACE) {
226         cairo_surface_t *surface;
227
228         if (DLCALL (cairo_pattern_get_surface, pattern, &surface) == CAIRO_STATUS_SUCCESS &&
229             DLCALL (cairo_surface_get_type, surface) == CAIRO_SURFACE_TYPE_TEE &&
230             DLCALL (cairo_surface_get_reference_count, surface) == 2)
231         {
232             fdr_remove_tee (fdr_tee_surface_index (surface, 0));
233         }
234     }
235
236     DLCALL (cairo_pattern_destroy, pattern);
237 }
238
239 cairo_surface_t *
240 cairo_get_target (cairo_t *cr)
241 {
242     cairo_surface_t *tee;
243
244     tee = DLCALL (cairo_get_target, cr);
245     return fdr_tee_surface_index (tee, 0);
246 }
247
248 cairo_surface_t *
249 cairo_get_group_target (cairo_t *cr)
250 {
251     cairo_surface_t *tee;
252
253     tee = DLCALL (cairo_get_group_target, cr);
254     return fdr_tee_surface_index (tee, 0);
255 }
256
257 cairo_pattern_t *
258 cairo_pattern_create_for_surface (cairo_surface_t *surface)
259 {
260     cairo_surface_t *tee;
261
262     tee = fdr_surface_get_tee (surface);
263     if (tee != NULL)
264         surface = tee;
265
266     return DLCALL (cairo_pattern_create_for_surface, surface);
267 }
268
269 cairo_status_t
270 cairo_pattern_get_surface (cairo_pattern_t *pattern,
271                            cairo_surface_t **surface)
272 {
273     cairo_status_t status;
274     cairo_surface_t *tee;
275
276     status = DLCALL (cairo_pattern_get_surface, pattern, surface);
277     if (status != CAIRO_STATUS_SUCCESS)
278         return status;
279
280     tee = fdr_surface_get_tee (*surface);
281     if (tee != NULL)
282         *surface = tee;
283
284     return CAIRO_STATUS_SUCCESS;
285 }
286
287 void
288 cairo_set_source_surface (cairo_t *cr,
289                           cairo_surface_t *surface,
290                           double x, double y)
291 {
292     cairo_surface_t *tee;
293
294     tee = fdr_surface_get_tee (surface);
295     if (tee != NULL)
296         surface = tee;
297
298     DLCALL (cairo_set_source_surface, cr, surface, x, y);
299 }
300
301 cairo_surface_t *
302 cairo_surface_create_similar (cairo_surface_t *surface,
303                               cairo_content_t content,
304                               int width, int height)
305 {
306     cairo_surface_t *tee;
307
308     tee = fdr_surface_get_tee (surface);
309     if (tee != NULL)
310         surface = tee;
311
312     return DLCALL (cairo_surface_create_similar,
313                    surface, content, width, height);
314 }
315
316 cairo_surface_t *
317 cairo_surface_create_for_rectangle (cairo_surface_t *surface,
318                                     double               x,
319                                     double               y,
320                                     double               width,
321                                     double               height)
322 {
323     cairo_surface_t *tee;
324
325     tee = fdr_surface_get_tee (surface);
326     if (tee != NULL)
327         surface = tee;
328
329     return DLCALL (cairo_surface_create_for_rectangle,
330                    surface, x, y, width, height);
331 }