26#ifdef HAVE_SYS_IOCTL_H 
   30#ifdef HAVE_SYS_SELECT_H 
   31#include <sys/select.h> 
   33#ifdef HAVE_SYS_INOTIFY_H 
   34#include <sys/inotify.h> 
   48#ifdef CAIRO_HAS_XLIB_SURFACE 
   50#include <X11/extensions/Xrender.h> 
   51#include <cairo-xlib.h> 
   53typedef struct window_xlib_s {
 
   61  Atom wm_delete_window_atom;
 
   64static void handle_configure_notify(
GVJ_t *job, XConfigureEvent *cev) {
 
   71  assert(cev->width >= 0 && 
"Xlib returned an event with negative width");
 
   72  assert(cev->height >= 0 && 
"Xlib returned an event with negative height");
 
   74  job->
zoom *= 1 + fmin(((
double)cev->width - job->
width) / job->
width,
 
   76  if ((
unsigned)cev->width > job->
width || (
unsigned)cev->height > job->
height)
 
   78  job->
width = (unsigned)cev->width;
 
   79  job->
height = (unsigned)cev->height;
 
   83static void handle_expose(
GVJ_t *job, XExposeEvent *eev) {
 
   87  assert(eev->width >= 0 &&
 
   88         "Xlib returned an expose event with negative width");
 
   89  assert(eev->height >= 0 &&
 
   90         "Xlib returned an expose event with negative height");
 
   91  XCopyArea(eev->display, window->pix, eev->window, window->gc, eev->x, eev->y,
 
   92            (
unsigned)eev->width, (
unsigned)eev->height, eev->x, eev->y);
 
   95static void handle_client_message(
GVJ_t *job, XClientMessageEvent *cmev) {
 
   99  if (cmev->format == 32 &&
 
  100      (Atom)cmev->data.l[0] == window->wm_delete_window_atom)
 
  104static bool handle_keypress(
GVJ_t *job, XKeyEvent *kev) {
 
  105  const KeyCode *
const keycodes = job->
keycodes;
 
  106  for (
size_t i = 0; i < job->
numkeys; i++) {
 
  107    if (kev->keycode == keycodes[i])
 
  113static Visual *find_argb_visual(Display *dpy, 
int scr) {
 
  115  XVisualInfo 
template;
 
  118  XRenderPictFormat *
format;
 
  121  template.screen = scr;
 
  123  template.class = TrueColor;
 
  125      XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask,
 
  130  for (i = 0; i < nvi; i++) {
 
  131    format = XRenderFindVisualFormat(dpy, xvi[i].visual);
 
  132    if (
format->type == PictTypeDirect && 
format->direct.alphaMask) {
 
  133      visual = xvi[i].visual;
 
  142static void browser_show(
GVJ_t *job) {
 
  143  char *exec_argv[3] = {BROWSER, 
NULL, 
NULL};
 
  150    fprintf(stderr, 
"fork failed: %s\n", strerror(errno));
 
  151  } 
else if (pid == 0) {
 
  152    execvp(exec_argv[0], exec_argv);
 
  153    fprintf(stderr, 
"error starting %s: %s\n", exec_argv[0], strerror(errno));
 
  157static int handle_xlib_events(
GVJ_t *firstjob, Display *dpy) {
 
  164  while (XPending(dpy)) {
 
  165    XNextEvent(dpy, &xev);
 
  167    for (job = firstjob; job; job = job->
next_active) {
 
  169      if (xev.xany.window == window->win) {
 
  170        switch (xev.xany.type) {
 
  172          pointer.
x = xev.xbutton.x;
 
  173          pointer.
y = xev.xbutton.y;
 
  174          assert(xev.xbutton.button <= INT_MAX &&
 
  175                 "Xlib returned invalid button event");
 
  181            pointer.
x = xev.xbutton.x;
 
  182            pointer.
y = xev.xbutton.y;
 
  188          pointer.
x = xev.xbutton.x;
 
  189          pointer.
y = xev.xbutton.y;
 
  190          assert(xev.xbutton.button <= INT_MAX &&
 
  191                 "Xlib returned invalid button event");
 
  194              xev.xbutton.button == 1)
 
  199          if (handle_keypress(job, &xev.xkey))
 
  203        case ConfigureNotify:
 
  204          handle_configure_notify(job, &xev.xconfigure);
 
  208          handle_expose(job, &xev.xexpose);
 
  212          handle_client_message(job, &xev.xclient);
 
  225static void update_display(
GVJ_t *job, Display *dpy) {
 
  227  cairo_surface_t *surface;
 
  232  assert(job->
width <= INT_MAX && 
"out of range width");
 
  233  assert(job->
height <= INT_MAX && 
"out of range height");
 
  236    XFreePixmap(dpy, window->pix);
 
  237    assert(window->depth >= 0 && 
"Xlib returned invalid window depth");
 
  238    window->pix = XCreatePixmap(dpy, window->win, job->
width, job->
height,
 
  239                                (
unsigned)window->depth);
 
  244    XFillRectangle(dpy, window->pix, window->gc, 0, 0, job->
width, job->
height);
 
  245    surface = cairo_xlib_surface_create(dpy, window->pix, window->visual,
 
  247    job->
context = cairo_create(surface);
 
  250    cairo_surface_destroy(surface);
 
  251    XCopyArea(dpy, window->pix, window->win, window->gc, 0, 0, job->
width,
 
  257static void init_window(
GVJ_t *job, Display *dpy, 
int scr) {
 
  259  const char *base = 
"";
 
  261  XSetWindowAttributes attributes;
 
  263  XSizeHints *normalhints;
 
  264  XClassHint *classhint;
 
  265  uint64_t attributemask = 0;
 
  269  window = 
malloc(
sizeof(window_t));
 
  270  if (window == 
NULL) {
 
  271    fprintf(stderr, 
"Failed to malloc window_t\n");
 
  279  zoom_to_fit = fmin((
double)w / job->
width, (
double)h / job->
height);
 
  280  if (zoom_to_fit < 1.0) 
 
  281    job->
zoom *= zoom_to_fit;
 
  290  if (argb && (window->visual = find_argb_visual(dpy, scr))) {
 
  292        XCreateColormap(dpy, RootWindow(dpy, scr), window->visual, AllocNone);
 
  293    attributes.override_redirect = False;
 
  294    attributes.background_pixel = 0;
 
  295    attributes.border_pixel = 0;
 
  296    attributes.colormap = window->cmap;
 
  298        (CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWColormap);
 
  301    window->cmap = DefaultColormap(dpy, scr);
 
  302    window->visual = DefaultVisual(dpy, scr);
 
  303    attributes.background_pixel = WhitePixel(dpy, scr);
 
  304    attributes.border_pixel = BlackPixel(dpy, scr);
 
  305    attributemask = (CWBackPixel | CWBorderPixel);
 
  306    window->depth = DefaultDepth(dpy, scr);
 
  309  window->win = XCreateWindow(dpy, RootWindow(dpy, scr), 0, 0, job->
width,
 
  310                              job->
height, 0, window->depth, InputOutput,
 
  311                              window->visual, attributemask, &attributes);
 
  316  normalhints = XAllocSizeHints();
 
  317  normalhints->flags = 0;
 
  320  assert(job->
width <= INT_MAX && 
"out of range width");
 
  321  normalhints->width = (int)job->
width;
 
  322  assert(job->
height <= INT_MAX && 
"out of range height");
 
  323  normalhints->height = (int)job->
height;
 
  325  classhint = XAllocClassHint();
 
  326  classhint->res_name = 
"graphviz";
 
  327  classhint->res_class = 
"Graphviz";
 
  329  wmhints = XAllocWMHints();
 
  330  wmhints->flags = InputHint;
 
  331  wmhints->input = True;
 
  333  Xutf8SetWMProperties(dpy, window->win, 
agxbuse(&name), base, 0, 0,
 
  334                       normalhints, wmhints, classhint);
 
  340  assert(window->depth >= 0 && 
"Xlib returned invalid window depth");
 
  341  window->pix = XCreatePixmap(dpy, window->win, job->
width, job->
height,
 
  342                              (
unsigned)window->depth);
 
  346    gcv.foreground = WhitePixel(dpy, scr);
 
  347  window->gc = XCreateGC(dpy, window->pix, GCForeground, &gcv);
 
  348  update_display(job, dpy);
 
  351      (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask |
 
  352       StructureNotifyMask | ExposureMask);
 
  353  XSelectInput(dpy, window->win, (
long)window->event_mask);
 
  354  window->wm_delete_window_atom = XInternAtom(dpy, 
"WM_DELETE_WINDOW", False);
 
  355  XSetWMProtocols(dpy, window->win, &window->wm_delete_window_atom, 1);
 
  356  XMapWindow(dpy, window->win);
 
  359static int handle_stdin_events(
GVJ_t *job) {
 
  368#ifdef HAVE_SYS_INOTIFY_H 
  369static int handle_file_events(
GVJ_t *job, 
int inotify_fd) {
 
  370  int avail, ret, 
len, rc = 0;
 
  372  struct inotify_event *event;
 
  374  ret = ioctl(inotify_fd, FIONREAD, &avail);
 
  376    fprintf(stderr, 
"ioctl() failed\n");
 
  381    assert(avail > 0 && 
"invalid value from FIONREAD");
 
  382    void *buf = 
malloc((
size_t)avail);
 
  384      fprintf(stderr, 
"out of memory (could not allocate %d bytes)\n", avail);
 
  387    len = (int)
read(inotify_fd, buf, (
size_t)avail);
 
  389      fprintf(stderr, 
"avail = %d, len = %d\n", avail, 
len);
 
  395      event = (
struct inotify_event *)bf;
 
  396      if (event->mask == IN_MODIFY) {
 
  402        if (strcmp(event->name, p) == 0) {
 
  407      size_t ln = event->len + 
sizeof(
struct inotify_event);
 
  408      assert(ln <= (
size_t)
len);
 
  414      fprintf(stderr, 
"length miscalculation, len = %d\n", 
len);
 
  422static void xlib_initialize(
GVJ_t *firstjob) {
 
  425  const char *display_name = 
NULL;
 
  428  Display *dpy = XOpenDisplay(display_name);
 
  430    fprintf(stderr, 
"Failed to open XLIB display: %s\n", XDisplayName(
NULL));
 
  433  scr = DefaultScreen(dpy);
 
  439    fprintf(stderr, 
"Failed to malloc %" PRISIZE_T "*KeyCode\n",
 
  444  for (
size_t i = 0; i < firstjob->
numkeys; i++) {
 
  446    if (keysym == NoSymbol)
 
  447      fprintf(stderr, 
"ERROR: No keysym for \"%s\"\n",
 
  450      keycodes[i] = XKeysymToKeycode(dpy, keysym);
 
  455      DisplayWidth(dpy, scr) * 25.4 / DisplayWidthMM(dpy, scr);
 
  457      DisplayHeight(dpy, scr) * 25.4 / DisplayHeightMM(dpy, scr);
 
  463static void xlib_finalize(
GVJ_t *firstjob) {
 
  465  Display *dpy = firstjob->
display;
 
  466  int scr = firstjob->
screen;
 
  467  KeyCode *keycodes = firstjob->
keycodes;
 
  468  int numfds, stdin_fd = 0, xlib_fd, ret, events;
 
  470  bool watching_stdin_p = 
false;
 
  471#ifdef HAVE_SYS_INOTIFY_H 
  474  bool watching_file_p = 
false;
 
  475  char *p, *cwd = 
NULL;
 
  477#ifdef HAVE_INOTIFY_INIT1 
  479  inotify_fd = inotify_init1(IN_CLOSEXEC);
 
  481  inotify_fd = inotify_init1(IN_CLOEXEC);
 
  484  inotify_fd = inotify_init();
 
  485  if (inotify_fd >= 0) {
 
  486    const int flags = fcntl(inotify_fd, F_GETFD);
 
  487    if (fcntl(inotify_fd, F_SETFD, 
flags | FD_CLOEXEC) < 0) {
 
  488      fprintf(stderr, 
"setting FD_CLOEXEC failed\n");
 
  493  if (inotify_fd < 0) {
 
  494    fprintf(stderr, 
"inotify_init() failed\n");
 
  504  numfds = xlib_fd = XConnectionNumber(dpy);
 
  508#ifdef HAVE_SYS_INOTIFY_H 
  509      watching_file_p = 
true;
 
  513        cwd = getcwd(
NULL, 0);
 
  520      p = strrchr(dirstr, 
'/');
 
  523      wd = inotify_add_watch(inotify_fd, dirstr, IN_MODIFY);
 
  526      numfds = 
imax(inotify_fd, numfds);
 
  530    watching_stdin_p = 
true;
 
  531#ifdef F_DUPFD_CLOEXEC 
  532    stdin_fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
 
  534    stdin_fd = fcntl(STDIN_FILENO, F_DUPFD, 0);
 
  535    (void)fcntl(stdin_fd, F_SETFD, fcntl(stdin_fd, F_GETFD) | FD_CLOEXEC);
 
  537    numfds = 
imax(stdin_fd, numfds);
 
  541    init_window(job, dpy, scr);
 
  548#ifdef HAVE_SYS_INOTIFY_H 
  549    if (watching_file_p) {
 
  550      if (FD_ISSET(inotify_fd, &rfds)) {
 
  551        ret = handle_file_events(firstjob, inotify_fd);
 
  556      FD_SET(inotify_fd, &rfds);
 
  560    if (watching_stdin_p) {
 
  561      if (FD_ISSET(stdin_fd, &rfds)) {
 
  562        ret = handle_stdin_events(firstjob);
 
  564          watching_stdin_p = 
false;
 
  565          FD_CLR(stdin_fd, &rfds);
 
  569      if (watching_stdin_p)
 
  570        FD_SET(stdin_fd, &rfds);
 
  573    ret = handle_xlib_events(firstjob, dpy);
 
  577    FD_SET(xlib_fd, &rfds);
 
  581        update_display(job, dpy);
 
  587      fprintf(stderr, 
"select() failed\n");
 
  592#ifdef HAVE_SYS_INOTIFY_H 
  594    ret = inotify_rm_watch(inotify_fd, wd);
 
  618#ifdef CAIRO_HAS_XLIB_SURFACE 
  619    {0, 
"xlib:cairo", 0, &device_engine_xlib, &device_features_xlib},
 
  620    {0, 
"x11:cairo", 0, &device_engine_xlib, &device_features_xlib},
 
 
static void agxbfree(agxbuf *xb)
free any malloced resources
 
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
 
static WUR char * agxbuse(agxbuf *xb)
 
static NORETURN void graphviz_exit(int status)
 
static double len(glCompPoint p)
 
Arithmetic helper functions.
 
static int imax(int a, int b)
maximum of two integers
 
#define GVDEVICE_DOES_TRUECOLOR
 
gvplugin_installed_t gvdevice_types_xlib[]
 
GVIO_API const char * format
 
gvdevice_callbacks_t * callbacks
 
gvevent_key_binding_t * keybindings
 
void(* button_release)(GVJ_t *job, int button, pointf pointer)
 
void(* refresh)(GVJ_t *job)
 
void(* button_press)(GVJ_t *job, int button, pointf pointer)
 
void(* motion)(GVJ_t *job, pointf pointer)
 
void(* read)(GVJ_t *job, const char *filename, const char *layout)
 
gvevent_key_callback_t callback