1 /* cairo-fdr - a 'flight data recorder', a black box, for cairo
3 * Copyright © 2009 Chris Wilson
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.
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.
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/>.
26 #include <cairo-script.h>
27 #include <cairo-tee.h>
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); \
45 (*name##_real) (args); \
48 #define RINGBUFFER_SIZE 16
49 static cairo_surface_t *fdr_ringbuffer[RINGBUFFER_SIZE];
50 static int fdr_position;
53 static const cairo_user_data_key_t fdr_key;
56 fdr_replay_to_script (cairo_surface_t *recording, cairo_device_t *ctx)
58 if (recording != NULL) {
59 DLCALL (cairo_script_write_comment, ctx, "--- fdr ---", -1);
60 DLCALL (cairo_script_from_recording_surface, ctx, recording);
65 fdr_dump_ringbuffer (void)
70 ctx = DLCALL (cairo_script_create, "/tmp/fdr.trace");
72 for (n = fdr_position; n < RINGBUFFER_SIZE; n++)
73 fdr_replay_to_script (fdr_ringbuffer[n], ctx);
75 for (n = 0; n < fdr_position; n++)
76 fdr_replay_to_script (fdr_ringbuffer[n], ctx);
78 DLCALL (cairo_device_destroy, ctx);
82 fdr_sighandler (int sig)
88 fdr_urgent_sighandler (int sig)
90 fdr_dump_ringbuffer ();
97 fdr_dump_ringbuffer ();
101 fdr_pending_signals (void)
103 static int initialized;
108 signal (SIGUSR1, fdr_sighandler);
110 signal (SIGSEGV, fdr_urgent_sighandler);
111 signal (SIGABRT, fdr_urgent_sighandler);
116 fdr_dump_ringbuffer ();
122 fdr_get_extents (cairo_surface_t *surface,
123 cairo_rectangle_t *extents)
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);
132 extents->width -= extents->x;
133 extents->height -= extents->y;
137 fdr_surface_destroy (void *surface)
139 DLCALL (cairo_surface_destroy, surface);
143 fdr_surface_reference (void *surface)
145 DLCALL (cairo_surface_reference, surface);
148 static cairo_surface_t *
149 fdr_surface_get_tee (cairo_surface_t *surface)
151 return DLCALL (cairo_surface_get_user_data, surface, &fdr_key);
154 static cairo_surface_t *
155 fdr_tee_surface_index (cairo_surface_t *surface, int index)
157 return DLCALL (cairo_tee_surface_index, surface, index);
161 cairo_create (cairo_surface_t *surface)
163 cairo_surface_t *record, *tee;
165 fdr_pending_signals ();
167 tee = fdr_surface_get_tee (surface);
169 cairo_rectangle_t extents;
170 cairo_content_t content;
172 fdr_get_extents (surface, &extents);
173 content = DLCALL (cairo_surface_get_content, surface);
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);
179 DLCALL (cairo_surface_set_user_data, surface,
180 &fdr_key, tee, fdr_surface_destroy);
184 record = fdr_tee_surface_index (tee, 1);
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;
195 fdr_surface_destroy (fdr_ringbuffer[fdr_position]);
196 fdr_ringbuffer[fdr_position] = record;
197 fdr_position = (fdr_position + 1) % RINGBUFFER_SIZE;
199 return DLCALL (cairo_create, tee);
203 fdr_remove_tee (cairo_surface_t *surface)
205 fdr_surface_reference (surface);
206 DLCALL (cairo_surface_set_user_data, surface, &fdr_key, NULL, NULL);
207 fdr_surface_destroy (surface);
211 cairo_destroy (cairo_t *cr)
213 cairo_surface_t *tee;
215 tee = DLCALL (cairo_get_target, cr);
216 DLCALL (cairo_destroy, cr);
218 if (DLCALL (cairo_surface_get_reference_count, tee) == 1)
219 fdr_remove_tee (fdr_tee_surface_index (tee, 0));
223 cairo_pattern_destroy (cairo_pattern_t *pattern)
225 if (DLCALL (cairo_pattern_get_type, pattern) == CAIRO_PATTERN_TYPE_SURFACE) {
226 cairo_surface_t *surface;
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)
232 fdr_remove_tee (fdr_tee_surface_index (surface, 0));
236 DLCALL (cairo_pattern_destroy, pattern);
240 cairo_get_target (cairo_t *cr)
242 cairo_surface_t *tee;
244 tee = DLCALL (cairo_get_target, cr);
245 return fdr_tee_surface_index (tee, 0);
249 cairo_get_group_target (cairo_t *cr)
251 cairo_surface_t *tee;
253 tee = DLCALL (cairo_get_group_target, cr);
254 return fdr_tee_surface_index (tee, 0);
258 cairo_pattern_create_for_surface (cairo_surface_t *surface)
260 cairo_surface_t *tee;
262 tee = fdr_surface_get_tee (surface);
266 return DLCALL (cairo_pattern_create_for_surface, surface);
270 cairo_pattern_get_surface (cairo_pattern_t *pattern,
271 cairo_surface_t **surface)
273 cairo_status_t status;
274 cairo_surface_t *tee;
276 status = DLCALL (cairo_pattern_get_surface, pattern, surface);
277 if (status != CAIRO_STATUS_SUCCESS)
280 tee = fdr_surface_get_tee (*surface);
284 return CAIRO_STATUS_SUCCESS;
288 cairo_set_source_surface (cairo_t *cr,
289 cairo_surface_t *surface,
292 cairo_surface_t *tee;
294 tee = fdr_surface_get_tee (surface);
298 DLCALL (cairo_set_source_surface, cr, surface, x, y);
302 cairo_surface_create_similar (cairo_surface_t *surface,
303 cairo_content_t content,
304 int width, int height)
306 cairo_surface_t *tee;
308 tee = fdr_surface_get_tee (surface);
312 return DLCALL (cairo_surface_create_similar,
313 surface, content, width, height);
317 cairo_surface_create_for_rectangle (cairo_surface_t *surface,
323 cairo_surface_t *tee;
325 tee = fdr_surface_get_tee (surface);
329 return DLCALL (cairo_surface_create_for_rectangle,
330 surface, x, y, width, height);