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