1 /*
2 * Copyright © 2009 CNRS
3 * Copyright © 2009-2012 Inria. All rights reserved.
4 * Copyright © 2009-2010 Université Bordeaux 1
5 * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
6 * See COPYING in top-level directory.
7 */
8
9 #include <private/autogen/config.h>
10
11 #include <cairo.h>
12
13 #if CAIRO_HAS_PDF_SURFACE
14 #include <cairo-pdf.h>
15 #endif /* CAIRO_HAS_PDF_SURFACE */
16
17 #if CAIRO_HAS_PS_SURFACE
18 #include <cairo-ps.h>
19 #endif /* CAIRO_HAS_PS_SURFACE */
20
21 #if CAIRO_HAS_SVG_SURFACE
22 #include <cairo-svg.h>
23 #endif /* CAIRO_HAS_SVG_SURFACE */
24
25 #ifndef HWLOC_HAVE_X11
26 /* In case X11 headers aren't availble, forcefully disable Cairo/Xlib. */
27 # undef CAIRO_HAS_XLIB_SURFACE
28 # define CAIRO_HAS_XLIB_SURFACE 0
29 #endif
30
31 #if CAIRO_HAS_XLIB_SURFACE
32 #include <cairo-xlib.h>
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/keysym.h>
36 #include <X11/cursorfont.h>
37 /* Avoid Xwindow's definition conflict with Windows' use for fields names. */
38 #undef Status
39 #endif /* CAIRO_HAS_XLIB_SURFACE */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <limits.h>
46
47 #include "lstopo.h"
48
49 #if (CAIRO_HAS_XLIB_SURFACE + CAIRO_HAS_PNG_FUNCTIONS + CAIRO_HAS_PDF_SURFACE + CAIRO_HAS_PS_SURFACE + CAIRO_HAS_SVG_SURFACE)
50 /* Cairo methods */
51 static void
topo_cairo_box(void * output,int r,int g,int b,unsigned depth __hwloc_attribute_unused,unsigned x,unsigned width,unsigned y,unsigned height)52 topo_cairo_box(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x, unsigned width, unsigned y, unsigned height)
53 {
54 cairo_t *c = output;
55 cairo_rectangle(c, x, y, width, height);
56 cairo_set_source_rgb(c, (float)r / 255, (float) g / 255, (float) b / 255);
57 cairo_fill(c);
58
59 cairo_rectangle(c, x, y, width, height);
60 cairo_set_source_rgb(c, 0, 0, 0);
61 cairo_set_line_width(c, 1);
62 cairo_stroke(c);
63 }
64
65 static void
topo_cairo_line(void * output,int r,int g,int b,unsigned depth __hwloc_attribute_unused,unsigned x1,unsigned y1,unsigned x2,unsigned y2)66 topo_cairo_line(void *output, int r, int g, int b, unsigned depth __hwloc_attribute_unused, unsigned x1, unsigned y1, unsigned x2, unsigned y2)
67 {
68 cairo_t *c = output;
69 cairo_move_to(c, x1, y1);
70 cairo_set_source_rgb(c, (float) r / 255, (float) g / 255, (float) b / 255);
71 cairo_set_line_width(c, 1);
72 cairo_line_to(c, x2, y2);
73 cairo_stroke(c);
74 }
75
76 static void
topo_cairo_text(void * output,int r,int g,int b,int size,unsigned depth __hwloc_attribute_unused,unsigned x,unsigned y,const char * text)77 topo_cairo_text(void *output, int r, int g, int b, int size, unsigned depth __hwloc_attribute_unused, unsigned x, unsigned y, const char *text)
78 {
79 cairo_t *c = output;
80 cairo_move_to(c, x, y + size);
81 cairo_set_font_size(c, size);
82 cairo_set_source_rgb(c, (float)r / 255, (float) g / 255, (float) b / 255);
83 cairo_show_text(c, text);
84 }
85
86 #if (CAIRO_HAS_PNG_FUNCTIONS + CAIRO_HAS_PDF_SURFACE + CAIRO_HAS_PS_SURFACE + CAIRO_HAS_SVG_SURFACE)
87 static cairo_status_t
topo_cairo_write(void * closure,const unsigned char * data,unsigned int length)88 topo_cairo_write(void *closure, const unsigned char *data, unsigned int length)
89 {
90 if (fwrite(data, length, 1, closure) < 1)
91 return CAIRO_STATUS_WRITE_ERROR;
92 return CAIRO_STATUS_SUCCESS;
93 }
94 #endif /* (CAIRO_HAS_PNG_FUNCTIONS + CAIRO_HAS_PDF_SURFACE + CAIRO_HAS_PS_SURFACE + CAIRO_HAS_SVG_SURFACE) */
95
96 static void
topo_cairo_paint(struct draw_methods * methods,int logical,int legend,hwloc_topology_t topology,cairo_surface_t * cs)97 topo_cairo_paint(struct draw_methods *methods, int logical, int legend, hwloc_topology_t topology, cairo_surface_t *cs)
98 {
99 cairo_t *c;
100 c = cairo_create(cs);
101 output_draw(methods, logical, legend, topology, c);
102 cairo_show_page(c);
103 cairo_destroy(c);
104 }
105
null_declare_color(void * output __hwloc_attribute_unused,int r __hwloc_attribute_unused,int g __hwloc_attribute_unused,int b __hwloc_attribute_unused)106 static void null_declare_color (void *output __hwloc_attribute_unused, int r __hwloc_attribute_unused, int g __hwloc_attribute_unused, int b __hwloc_attribute_unused) {}
107 #endif /* (CAIRO_HAS_XLIB_SURFACE + CAIRO_HAS_PNG_FUNCTIONS + CAIRO_HAS_PDF_SURFACE + CAIRO_HAS_PS_SURFACE + CAIRO_HAS_SVG_SURFACE) */
108
109
110 #if CAIRO_HAS_XLIB_SURFACE
111 /* X11 back-end */
112
113 struct display {
114 Display *dpy;
115 int scr;
116 cairo_surface_t *cs;
117 Window top, win;
118 Cursor hand;
119 unsigned int orig_fontsize, orig_gridsize;
120 int screen_width, screen_height; /** visible part size */
121 int last_screen_width, last_screen_height; /** last visible part size */
122 int width, height; /** total normal display size */
123 int x, y; /** top left corner of the visible part */
124 };
125
126 static void
x11_create(struct display * disp,int width,int height)127 x11_create(struct display *disp, int width, int height)
128 {
129 disp->win = XCreateSimpleWindow(disp->dpy, disp->top, 0, 0, width, height, 0, WhitePixel(disp->dpy, disp->scr), WhitePixel(disp->dpy, disp->scr));
130 disp->hand = XCreateFontCursor(disp->dpy, XC_fleur);
131 XDefineCursor(disp->dpy, disp->win, disp->hand);
132 XSelectInput(disp->dpy, disp->win,
133 KeyPressMask |
134 ButtonPressMask | ButtonReleaseMask |
135 PointerMotionMask |
136 ExposureMask);
137 XMapWindow(disp->dpy, disp->win);
138 disp->cs = cairo_xlib_surface_create(disp->dpy, disp->win, DefaultVisual(disp->dpy, disp->scr), width, height);
139
140 }
141
142 static void
x11_destroy(struct display * disp)143 x11_destroy(struct display *disp)
144 {
145 cairo_surface_destroy(disp->cs);
146 XDestroyWindow(disp->dpy, disp->win);
147 }
148
149 static void *
x11_start(void * output __hwloc_attribute_unused,int width,int height)150 x11_start(void *output __hwloc_attribute_unused, int width, int height)
151 {
152 Display *dpy;
153 Window root, top;
154 int scr;
155 Screen *screen;
156 int screen_width = width, screen_height = height;
157 struct display *disp;
158
159 if (!(dpy = XOpenDisplay(NULL))) {
160 fprintf(stderr, "couldn't connect to X\n");
161 exit(EXIT_FAILURE);
162 }
163
164 disp = malloc(sizeof(*disp));
165 disp->dpy = dpy;
166 disp->scr = scr = DefaultScreen(dpy);
167
168 screen = ScreenOfDisplay(dpy, scr);
169 if (screen_width >= screen->width)
170 screen_width = screen->width;
171 if (screen_height >= screen->height)
172 screen_height = screen->height;
173 disp->screen_width = screen_width;
174 disp->screen_height = screen_height;
175 disp->width = width;
176 disp->height = height;
177 disp->orig_fontsize = fontsize;
178 disp->orig_gridsize = gridsize;
179 disp->x = 0;
180 disp->y = 0;
181
182 root = RootWindow(dpy, scr);
183 disp->top = top = XCreateSimpleWindow(dpy, root, 0, 0, screen_width, screen_height, 0, WhitePixel(dpy, scr), WhitePixel(dpy, scr));
184 XSelectInput(dpy,top, StructureNotifyMask);
185 XMapWindow(dpy, top);
186
187 x11_create(disp, width, height);
188
189 return disp;
190 }
191
192 static struct draw_methods x11_draw_methods = {
193 x11_start,
194 null_declare_color,
195 topo_cairo_box,
196 topo_cairo_line,
197 topo_cairo_text,
198 };
199
200 /** Clip coordinates of the visible part. */
201 static void
move_x11(struct display * disp,int logical,int legend,hwloc_topology_t topology)202 move_x11(struct display *disp, int logical, int legend, hwloc_topology_t topology)
203 {
204 if (disp->width <= disp->screen_width) {
205 disp->x = 0;
206 } else {
207 if (disp->x < 0)
208 disp->x = 0;
209 if (disp->x >= disp->width - disp->screen_width)
210 disp->x = disp->width - disp->screen_width;
211 }
212
213 if (disp->height <= disp->screen_height) {
214 disp->y = 0;
215 } else {
216 if (disp->y < 0)
217 disp->y = 0;
218 if (disp->y >= disp->height - disp->screen_height)
219 disp->y = disp->height - disp->screen_height;
220 }
221
222 if (disp->screen_width > disp->width && disp->screen_height > disp->height
223 && (disp->screen_width != disp->last_screen_width
224 || disp->screen_height != disp->last_screen_height)) {
225 disp->last_screen_width = disp->screen_width;
226 disp->last_screen_height = disp->screen_height;
227 fontsize = disp->orig_fontsize;
228 gridsize = disp->orig_gridsize;
229 if (disp->screen_width > disp->width) {
230 fontsize = disp->orig_fontsize * disp->screen_width / disp->width;
231 gridsize = disp->orig_gridsize * disp->screen_width / disp->width;
232 }
233 if (disp->screen_height > disp->height) {
234 unsigned int new_fontsize = disp->orig_fontsize * disp->screen_height / disp->height;
235 unsigned int new_gridsize = disp->orig_gridsize * disp->screen_height / disp->height;
236 if (new_fontsize < fontsize)
237 fontsize = new_fontsize;
238 if (new_gridsize < gridsize)
239 gridsize = new_gridsize;
240 }
241
242 x11_destroy(disp);
243 x11_create(disp, disp->screen_width, disp->screen_height);
244 topo_cairo_paint(&x11_draw_methods, logical, legend, topology, disp->cs);
245 }
246 }
247
248 void
output_x11(hwloc_topology_t topology,const char * filename __hwloc_attribute_unused,int logical,int legend,int verbose_mode __hwloc_attribute_unused)249 output_x11(hwloc_topology_t topology, const char *filename __hwloc_attribute_unused, int logical, int legend, int verbose_mode __hwloc_attribute_unused)
250 {
251 struct display *disp = output_draw_start(&x11_draw_methods, logical, legend, topology, NULL);
252 int finish = 0;
253 int state = 0;
254 int x = 0, y = 0; /* shut warning down */
255 int lastx = disp->x, lasty = disp->y;
256
257 topo_cairo_paint(&x11_draw_methods, logical, legend, topology, disp->cs);
258
259 while (!finish) {
260 XEvent e;
261 if (!XEventsQueued(disp->dpy, QueuedAfterFlush)) {
262 /* No pending event, flush moving windows before waiting for next event */
263 if (disp->x != lastx || disp->y != lasty) {
264 XMoveWindow(disp->dpy, disp->win, -disp->x, -disp->y);
265 lastx = disp->x;
266 lasty = disp->y;
267 }
268 }
269 XNextEvent(disp->dpy, &e);
270 switch (e.type) {
271 case Expose:
272 if (e.xexpose.count < 1)
273 topo_cairo_paint(&x11_draw_methods, logical, legend, topology, disp->cs);
274 break;
275 case MotionNotify:
276 if (state) {
277 disp->x -= e.xmotion.x_root - x;
278 disp->y -= e.xmotion.y_root - y;
279 x = e.xmotion.x_root;
280 y = e.xmotion.y_root;
281 move_x11(disp, logical, legend, topology);
282 }
283 break;
284 case ConfigureNotify:
285 disp->screen_width = e.xconfigure.width;
286 disp->screen_height = e.xconfigure.height;
287 move_x11(disp, logical, legend, topology);
288 if (disp->x != lastx || disp->y != lasty)
289 XMoveWindow(disp->dpy, disp->win, -disp->x, -disp->y);
290 break;
291 case ButtonPress:
292 if (e.xbutton.button == Button1) {
293 state = 1;
294 x = e.xbutton.x_root;
295 y = e.xbutton.y_root;
296 }
297 break;
298 case ButtonRelease:
299 if (e.xbutton.button == Button1)
300 state = 0;
301 break;
302 case MappingNotify:
303 XRefreshKeyboardMapping(&e.xmapping);
304 break;
305 case KeyPress: {
306 KeySym keysym;
307 XLookupString(&e.xkey, NULL, 0, &keysym, NULL);
308 switch (keysym) {
309 case XK_q:
310 case XK_Q:
311 case XK_Escape:
312 finish = 1;
313 break;
314 case XK_Left:
315 disp->x -= disp->screen_width/10;
316 move_x11(disp, logical, legend, topology);
317 break;
318 case XK_Right:
319 disp->x += disp->screen_width/10;
320 move_x11(disp, logical, legend, topology);
321 break;
322 case XK_Up:
323 disp->y -= disp->screen_height/10;
324 move_x11(disp, logical, legend, topology);
325 break;
326 case XK_Down:
327 disp->y += disp->screen_height/10;
328 move_x11(disp, logical, legend, topology);
329 break;
330 case XK_Page_Up:
331 if (e.xkey.state & ControlMask) {
332 disp->x -= disp->screen_width;
333 move_x11(disp, logical, legend, topology);
334 } else {
335 disp->y -= disp->screen_height;
336 move_x11(disp, logical, legend, topology);
337 }
338 break;
339 case XK_Page_Down:
340 if (e.xkey.state & ControlMask) {
341 disp->x += disp->screen_width;
342 move_x11(disp, logical, legend, topology);
343 } else {
344 disp->y += disp->screen_height;
345 move_x11(disp, logical, legend, topology);
346 }
347 break;
348 case XK_Home:
349 disp->x = 0;
350 disp->y = 0;
351 move_x11(disp, logical, legend, topology);
352 break;
353 case XK_End:
354 disp->x = INT_MAX;
355 disp->y = INT_MAX;
356 move_x11(disp, logical, legend, topology);
357 break;
358 }
359 break;
360 }
361 }
362 }
363 x11_destroy(disp);
364 XDestroyWindow(disp->dpy, disp->top);
365 XFreeCursor(disp->dpy, disp->hand);
366 XCloseDisplay(disp->dpy);
367 free(disp);
368 }
369 #endif /* CAIRO_HAS_XLIB_SURFACE */
370
371
372 #if CAIRO_HAS_PNG_FUNCTIONS
373 /* PNG back-end */
374 static void *
png_start(void * output __hwloc_attribute_unused,int width,int height)375 png_start(void *output __hwloc_attribute_unused, int width, int height)
376 {
377 return cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
378 }
379
380 static struct draw_methods png_draw_methods = {
381 png_start,
382 null_declare_color,
383 topo_cairo_box,
384 topo_cairo_line,
385 topo_cairo_text,
386 };
387
388 void
output_png(hwloc_topology_t topology,const char * filename,int logical,int legend,int verbose_mode __hwloc_attribute_unused)389 output_png(hwloc_topology_t topology, const char *filename, int logical, int legend, int verbose_mode __hwloc_attribute_unused)
390 {
391 FILE *output = open_file(filename, "w");
392 cairo_surface_t *cs;
393
394 if (!output) {
395 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
396 return;
397 }
398
399 cs = output_draw_start(&png_draw_methods, logical, legend, topology, output);
400
401 topo_cairo_paint(&png_draw_methods, logical, legend, topology, cs);
402 cairo_surface_write_to_png_stream(cs, topo_cairo_write, output);
403 cairo_surface_destroy(cs);
404
405 if (output != stdout)
406 fclose(output);
407 }
408 #endif /* CAIRO_HAS_PNG_FUNCTIONS */
409
410
411 #if CAIRO_HAS_PDF_SURFACE
412 /* PDF back-end */
413 static void *
pdf_start(void * output,int width,int height)414 pdf_start(void *output, int width, int height)
415 {
416 return cairo_pdf_surface_create_for_stream(topo_cairo_write, output, width, height);
417 }
418
419 static struct draw_methods pdf_draw_methods = {
420 pdf_start,
421 null_declare_color,
422 topo_cairo_box,
423 topo_cairo_line,
424 topo_cairo_text,
425 };
426
427 void
output_pdf(hwloc_topology_t topology,const char * filename,int logical,int legend,int verbose_mode __hwloc_attribute_unused)428 output_pdf(hwloc_topology_t topology, const char *filename, int logical, int legend, int verbose_mode __hwloc_attribute_unused)
429 {
430 FILE *output = open_file(filename, "w");
431 cairo_surface_t *cs;
432
433 if (!output) {
434 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
435 return;
436 }
437
438 cs = output_draw_start(&pdf_draw_methods, logical, legend, topology, output);
439
440 topo_cairo_paint(&pdf_draw_methods, logical, legend, topology, cs);
441 cairo_surface_flush(cs);
442 cairo_surface_destroy(cs);
443
444 if (output != stdout)
445 fclose(output);
446 }
447 #endif /* CAIRO_HAS_PDF_SURFACE */
448
449
450 #if CAIRO_HAS_PS_SURFACE
451 /* PS back-end */
452 static void *
ps_start(void * output,int width,int height)453 ps_start(void *output, int width, int height)
454 {
455 return cairo_ps_surface_create_for_stream(topo_cairo_write, output, width, height);
456 }
457
458 static struct draw_methods ps_draw_methods = {
459 ps_start,
460 null_declare_color,
461 topo_cairo_box,
462 topo_cairo_line,
463 topo_cairo_text,
464 };
465
466 void
output_ps(hwloc_topology_t topology,const char * filename,int logical,int legend,int verbose_mode __hwloc_attribute_unused)467 output_ps(hwloc_topology_t topology, const char *filename, int logical, int legend, int verbose_mode __hwloc_attribute_unused)
468 {
469 FILE *output = open_file(filename, "w");
470 cairo_surface_t *cs;
471
472 if (!output) {
473 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
474 return;
475 }
476
477 cs = output_draw_start(&ps_draw_methods, logical, legend, topology, output);
478
479 topo_cairo_paint(&ps_draw_methods, logical, legend, topology, cs);
480 cairo_surface_flush(cs);
481 cairo_surface_destroy(cs);
482
483 if (output != stdout)
484 fclose(output);
485 }
486 #endif /* CAIRO_HAS_PS_SURFACE */
487
488
489 #if CAIRO_HAS_SVG_SURFACE
490 /* SVG back-end */
491 static void *
svg_start(void * output,int width,int height)492 svg_start(void *output, int width, int height)
493 {
494 return cairo_svg_surface_create_for_stream(topo_cairo_write, output, width, height);
495 }
496
497 static struct draw_methods svg_draw_methods = {
498 svg_start,
499 null_declare_color,
500 topo_cairo_box,
501 topo_cairo_line,
502 topo_cairo_text,
503 };
504
505 void
output_svg(hwloc_topology_t topology,const char * filename,int logical,int legend,int verbose_mode __hwloc_attribute_unused)506 output_svg(hwloc_topology_t topology, const char *filename, int logical, int legend, int verbose_mode __hwloc_attribute_unused)
507 {
508 FILE *output;
509 cairo_surface_t *cs;
510
511 output = open_file(filename, "w");
512 if (!output) {
513 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
514 return;
515 }
516
517 cs = output_draw_start(&svg_draw_methods, logical, legend, topology, output);
518
519 topo_cairo_paint(&svg_draw_methods, logical, legend, topology, cs);
520 cairo_surface_flush(cs);
521 cairo_surface_destroy(cs);
522
523 if (output != stdout)
524 fclose(output);
525 }
526 #endif /* CAIRO_HAS_SVG_SURFACE */
527