From: ellson Date: Tue, 11 Mar 2008 18:55:46 +0000 (+0000) Subject: make a start at a glitz plugin X-Git-Tag: LAST_LIBGRAPH~32^2~4515 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9dd560d439bcc9e923d2e963bd179ee07f364678;p=graphviz make a start at a glitz plugin --- diff --git a/plugin/glitz/gvdevice_glitz.c b/plugin/glitz/gvdevice_glitz.c new file mode 100644 index 000000000..1c8b0c202 --- /dev/null +++ b/plugin/glitz/gvdevice_glitz.c @@ -0,0 +1,623 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_INOTIFY_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "gvplugin_device.h" + +#include +#ifdef CAIRO_HAS_GLITZ_SURFACE +#include + +#ifdef GLITZ_GLX_BACKEND +#include +#endif + +#ifdef GLITZ_AGL_BACKEND +#include +#include +/* Ugly hack to avoid conflicts between Xlib and MacOS */ +#undef CAIRO_HAS_XLIB_SURFACE +#endif + +#ifdef CAIRO_HAS_XLIB_SURFACE +#include +#include +#include +#include +#endif + +/* Don't load up the glitz plugin with all that gnome stuff */ +/* - OTOH now we are dependent on a firefox installation */ +/* */ +/* This undef must follow gvplugin_device.h because */ +/* config.h is reincluded via: */ +/* types.h->cdt.h->ast_common.h */ +/* FIXME */ +#undef HAVE_GNOMEUI +#define BROWSER "firefox" + +#ifdef HAVE_GNOMEUI +#include +#endif + +typedef struct window_glitz_s { + Window win; + unsigned long event_mask; + Pixmap pix; + GC gc; + Visual *visual; + Colormap cmap; + int depth; + Atom wm_delete_window_atom; +} window_t; + +static void handle_configure_notify(GVJ_t * job, XConfigureEvent * cev) +{ +/*FIXME - should allow for margins */ +/* - similar zoom_to_fit code exists in: */ +/* plugin/gtk/callbacks.c */ +/* plugin/glitz/gvdevice_glitz.c */ +/* lib/gvc/gvevent.c */ + + if (job->fit_mode) + job->zoom = MIN((double) cev->width / (double) job->width, + (double) cev->height / (double) job->height); + if (cev->width > job->width || cev->height > job->height) + job->has_grown = 1; + job->width = cev->width; + job->height = cev->height; + job->needs_refresh = 1; +} + +static void handle_expose(GVJ_t * job, XExposeEvent * eev) +{ + window_t *window; + + window = (window_t *)job->window; + XCopyArea(eev->display, window->pix, eev->window, window->gc, + eev->x, eev->y, eev->width, eev->height, eev->x, eev->y); +} + +static void handle_client_message(GVJ_t * job, XClientMessageEvent * cmev) +{ + window_t *window; + + window = (window_t *)job->window; + if (cmev->format == 32 + && (Atom) cmev->data.l[0] == window->wm_delete_window_atom) + exit(0); +} + +static boolean handle_keypress(GVJ_t *job, XKeyEvent *kev) +{ + + int i; + KeyCode *keycodes; + + keycodes = (KeyCode *)job->keycodes; + for (i=0; i < job->numkeys; i++) { + if (kev->keycode == keycodes[i]) + return (job->keybindings[i].callback)(job); + } + return FALSE; +} + +static Visual *find_argb_visual(Display * dpy, int scr) +{ + XVisualInfo *xvi; + XVisualInfo template; + int nvi; + int i; + XRenderPictFormat *format; + Visual *visual; + + template.screen = scr; + template.depth = 32; + template.class = TrueColor; + xvi = XGetVisualInfo(dpy, + VisualScreenMask | + VisualDepthMask | + VisualClassMask, &template, &nvi); + if (!xvi) + return 0; + visual = 0; + for (i = 0; i < nvi; i++) { + format = XRenderFindVisualFormat(dpy, xvi[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) { + visual = xvi[i].visual; + break; + } + } + + XFree(xvi); + return visual; +} + +static void browser_show(GVJ_t *job) +{ +#ifdef HAVE_GNOMEUI + gnome_url_show(job->selected_href, NULL); +#else +#if defined HAVE_SYS_TYPES_H && defined HAVE_UNISTD_H && defined HAVE_ERRNO_H + char *exec_argv[3] = {BROWSER, NULL, NULL}; + pid_t pid; + int err; + + exec_argv[1] = job->selected_href; + + pid = fork(); + if (pid == -1) { + fprintf(stderr,"fork failed: %s\n", strerror(errno)); + } + else if (pid == 0) { + err = execvp(exec_argv[0], exec_argv); + fprintf(stderr,"error starting %s: %s\n", exec_argv[0], strerror(errno)); + } +#else + fprintf(stdout,"browser_show: %s\n", job->selected_href); +#endif +#endif +} + +static int handle_glitz_events (GVJ_t *firstjob, Display *dpy) +{ + GVJ_t *job; + window_t *window; + XEvent xev; + pointf pointer; + int rc = 0; + + while (XPending(dpy)) { + XNextEvent(dpy, &xev); + + for (job = firstjob; job; job = job->next_active) { + window = (window_t *)job->window; + if (xev.xany.window == window->win) { + switch (xev.xany.type) { + case ButtonPress: + pointer.x = (double)xev.xbutton.x; + pointer.y = (double)xev.xbutton.y; + (job->callbacks->button_press)(job, xev.xbutton.button, pointer); + rc++; + break; + case MotionNotify: + if (job->button) { /* only interested while a button is pressed */ + pointer.x = (double)xev.xbutton.x; + pointer.y = (double)xev.xbutton.y; + (job->callbacks->motion)(job, pointer); + rc++; + } + break; + case ButtonRelease: + pointer.x = (double)xev.xbutton.x; + pointer.y = (double)xev.xbutton.y; + (job->callbacks->button_release)(job, xev.xbutton.button, pointer); + if (job->selected_href && job->selected_href[0] && xev.xbutton.button == 1) + browser_show(job); + rc++; + break; + case KeyPress: + if (handle_keypress(job, &xev.xkey)) + return -1; /* exit code */ + break; + case ConfigureNotify: + handle_configure_notify(job, &xev.xconfigure); + rc++; + break; + case Expose: + handle_expose(job, &xev.xexpose); + rc++; + break; + case ClientMessage: + handle_client_message(job, &xev.xclient); + rc++; + break; + } + break; + } + } + } + return rc; +} + +static void update_display(GVJ_t *job, Display *dpy) +{ + window_t *window; + cairo_surface_t *surface; + + window = (window_t *)job->window; + + if (job->has_grown) { + XFreePixmap(dpy, window->pix); + window->pix = XCreatePixmap(dpy, window->win, + job->width, job->height, window->depth); + job->has_grown = 0; + job->needs_refresh = 1; + } + if (job->needs_refresh) { + XFillRectangle(dpy, window->pix, window->gc, 0, 0, + job->width, job->height); + surface = cairo_xlib_surface_create(dpy, + window->pix, window->visual, + job->width, job->height); + job->context = (void *)cairo_create(surface); + job->external_context = TRUE; + (job->callbacks->refresh)(job); + cairo_surface_destroy(surface); + XCopyArea(dpy, window->pix, window->win, window->gc, + 0, 0, job->width, job->height, 0, 0); + job->needs_refresh = 0; + } +} + +static void init_window(GVJ_t *job, Display *dpy, int scr) +{ + int argb = 0; + const char *base = ""; + XGCValues gcv; + XSetWindowAttributes attributes; + XWMHints *wmhints; + XSizeHints *normalhints; + XClassHint *classhint; + unsigned long attributemask = 0; + char *name; + window_t *window; + + window = (window_t *)malloc(sizeof(window_t)); + if (window == NULL) { + fprintf(stderr, "Failed to malloc window_t\n"); + return; + } + job->window = (void *)window; + job->fit_mode = 0; + job->needs_refresh = 1; + + if (argb && (window->visual = find_argb_visual(dpy, scr))) { + window->cmap = XCreateColormap(dpy, RootWindow(dpy, scr), + window->visual, AllocNone); + attributes.override_redirect = False; + attributes.background_pixel = 0; + attributes.border_pixel = 0; + attributes.colormap = window->cmap; + attributemask = ( CWBackPixel + | CWBorderPixel + | CWOverrideRedirect + | CWColormap ); + window->depth = 32; + } else { + window->cmap = DefaultColormap(dpy, scr); + window->visual = DefaultVisual(dpy, scr); + attributes.background_pixel = WhitePixel(dpy, scr); + attributes.border_pixel = BlackPixel(dpy, scr); + attributemask = (CWBackPixel | CWBorderPixel); + window->depth = DefaultDepth(dpy, scr); + } + + window->win = XCreateWindow(dpy, RootWindow(dpy, scr), + 0, 0, job->width, job->height, 0, window->depth, + InputOutput, window->visual, + attributemask, &attributes); + + name = malloc(strlen("graphviz: ") + strlen(base) + 1); + strcpy(name, "graphviz: "); + strcat(name, base); + + normalhints = XAllocSizeHints(); + normalhints->flags = 0; + normalhints->x = 0; + normalhints->y = 0; + normalhints->width = job->width; + normalhints->height = job->height; + + classhint = XAllocClassHint(); + classhint->res_name = "graphviz"; + classhint->res_class = "Graphviz"; + + wmhints = XAllocWMHints(); + wmhints->flags = InputHint; + wmhints->input = True; + + Xutf8SetWMProperties(dpy, window->win, name, base, 0, 0, + normalhints, wmhints, classhint); + XFree(wmhints); + XFree(classhint); + XFree(normalhints); + free(name); + + window->pix = XCreatePixmap(dpy, window->win, job->width, job->height, + window->depth); + if (argb) + gcv.foreground = 0; + else + gcv.foreground = WhitePixel(dpy, scr); + window->gc = XCreateGC(dpy, window->pix, GCForeground, &gcv); + update_display(job, dpy); + + window->event_mask = ( + ButtonPressMask + | ButtonReleaseMask + | PointerMotionMask + | KeyPressMask + | StructureNotifyMask + | ExposureMask); + XSelectInput(dpy, window->win, window->event_mask); + window->wm_delete_window_atom = + XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, window->win, &window->wm_delete_window_atom, 1); + XMapWindow(dpy, window->win); +} + +#ifdef HAVE_SYS_INOTIFY_H +static int handle_file_events(GVJ_t *job, int inotify_fd) +{ + int avail, ret, len, ln, rc = 0; + static char *buf; + char *bf, *p; + struct inotify_event *event; + + ret = ioctl(inotify_fd, FIONREAD, &avail); + if (ret < 0) { + fprintf(stderr,"ioctl() failed\n"); + return -1;; + } + + if (avail) { + buf = realloc(buf, avail); + if (!buf) { + fprintf(stderr,"problem with realloc(%d)\n", avail); + return -1; + } + len = read(inotify_fd, buf, avail); + if (len != avail) { + fprintf(stderr,"avail = %u, len = %u\n", avail, len); + return -1; + } + bf = buf; + while (len > 0) { + event = (struct inotify_event *)bf; + switch (event->mask) { + case IN_MODIFY: + p = strrchr(job->input_filename, '/'); + if (p) + p++; + else + p = job->input_filename; + if (strcmp((char*)(&(event->name)), p) == 0) { + (job->callbacks->read)(job, job->input_filename, job->layout_type); + rc++; + } + break; + + case IN_ACCESS: + case IN_ATTRIB: + case IN_CLOSE_WRITE: + case IN_CLOSE_NOWRITE: + case IN_OPEN: + case IN_MOVED_FROM: + case IN_MOVED_TO: + case IN_CREATE: + case IN_DELETE: + case IN_DELETE_SELF: + case IN_MOVE_SELF: + case IN_UNMOUNT: + case IN_Q_OVERFLOW: + case IN_IGNORED: + case IN_ISDIR: + case IN_ONESHOT: + break; + } + ln = event->len + 4 * sizeof(int); + bf += ln; + len -= ln; + } + if (len != 0) { + fprintf(stderr,"length miscalculation, len = %d\n", len); + return -1; + } + } + return rc; +} +#endif + +static void glitz_initialize(GVJ_t *firstjob) +{ + Display *dpy; + KeySym keysym; + KeyCode *keycodes; + const char *display_name = NULL; + int i, scr; + + dpy = XOpenDisplay(display_name); + if (dpy == NULL) { + fprintf(stderr, "Failed to open XLIB display: %s\n", + XDisplayName(NULL)); + return; + } + scr = DefaultScreen(dpy); + + firstjob->display = (void*)dpy; + firstjob->screen = scr; + + keycodes = (KeyCode *)malloc(firstjob->numkeys * sizeof(KeyCode)); + if (keycodes == NULL) { + fprintf(stderr, "Failed to malloc %d*KeyCode\n", firstjob->numkeys); + return; + } + for (i = 0; i < firstjob->numkeys; i++) { + keysym = XStringToKeysym(firstjob->keybindings[i].keystring); + if (keysym == NoSymbol) + fprintf(stderr, "ERROR: No keysym for \"%s\"\n", + firstjob->keybindings[i].keystring); + else + keycodes[i] = XKeysymToKeycode(dpy, keysym); + } + firstjob->keycodes = (void*)keycodes; + + firstjob->device_dpi.x = DisplayWidth(dpy, scr) * 25.4 / DisplayWidthMM(dpy, scr); + firstjob->device_dpi.y = DisplayHeight(dpy, scr) * 25.4 / DisplayHeightMM(dpy, scr); + firstjob->device_sets_dpi = TRUE; +} + +static void glitz_finalize(GVJ_t *firstjob) +{ + GVJ_t *job; + Display *dpy = (Display *)(firstjob->display); + int scr = firstjob->screen; + KeyCode *keycodes= firstjob->keycodes; + int inotify_fd=0, glitz_fd, ret, events; + fd_set rfds; + struct timeval timeout; +#ifdef HAVE_SYS_INOTIFY_H + int wd=0; + boolean watching_p = FALSE; + static char *dir; + char *p, *cwd = NULL; + + inotify_fd = inotify_init(); + if (inotify_fd < 0) { + fprintf(stderr,"inotify_init() failed\n"); + return; + } + + /* test that we have access to the input filename */ + if (firstjob->input_filename && firstjob->graph_index == 0) { + + watching_p = TRUE; + + if (firstjob->input_filename[0] != '/') { + cwd = getcwd(NULL, 0); + dir = realloc(dir, strlen(cwd) + 1 + strlen(firstjob->input_filename) + 1); + strcpy(dir, cwd); + strcat(dir, "/"); + strcat(dir, firstjob->input_filename); + free(cwd); + } + else { + dir = realloc(dir, strlen(firstjob->input_filename) + 1); + strcpy(dir, firstjob->input_filename); + } + p = strrchr(dir,'/'); + *p = '\0'; + + wd = inotify_add_watch(inotify_fd, dir, IN_MODIFY ); + } +#endif + + for (job = firstjob; job; job = job->next_active) + init_window(job, dpy, scr); + + ret = handle_glitz_events(firstjob, dpy); + + /* FIXME - poll for initial expose event */ + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + select(0, NULL, NULL, NULL, &timeout); + + glitz_fd = XConnectionNumber(dpy); + + /* This is the event loop */ + FD_ZERO(&rfds); + while (1) { + events = 0; + +#ifdef HAVE_SYS_INOTIFY_H + ret = handle_file_events(firstjob, inotify_fd); + if (ret < 0) + break; + events += ret; + FD_SET(inotify_fd, &rfds); +#endif + + ret = handle_glitz_events(firstjob, dpy); + if (ret < 0) + break; + events += ret; + FD_SET(glitz_fd, &rfds); + + if (events) + for (job = firstjob; job; job = job->next_active) + update_display(job, dpy); + + ret = select(MAX(inotify_fd, glitz_fd)+1, &rfds, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr,"select() failed\n"); + break; + } + } + +#ifdef HAVE_SYS_INOTIFY_H + if (watching_p) + ret = inotify_rm_watch(inotify_fd, wd); +#endif + + XCloseDisplay(dpy); + free(keycodes); + firstjob->keycodes = NULL; +} + +static gvdevice_features_t device_features_glitz = { + GVDEVICE_DOES_TRUECOLOR + | GVDEVICE_EVENTS, /* flags */ + {0.,0.}, /* default margin - points */ + {0.,0.}, /* default page width, height - points */ + {96.,96.}, /* dpi */ +}; + +static gvdevice_engine_t device_engine_glitz = { + glitz_initialize, + NULL, /* glitz_format */ + glitz_finalize, +}; +#endif /* CAIRO_HAS_GLITZ_SURFACE */ + +gvplugin_installed_t gvdevice_types_glitz[] = { +#ifdef CAIRO_HAS_GLITZ_SURFACE + {0, "glitz:cairo", 0, &device_engine_glitz, &device_features_glitz}, +#endif + {0, NULL, 0, NULL, NULL} +}; diff --git a/plugin/glitz/gvplugin_glitz.c b/plugin/glitz/gvplugin_glitz.c new file mode 100644 index 000000000..46a2a6c4f --- /dev/null +++ b/plugin/glitz/gvplugin_glitz.c @@ -0,0 +1,26 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#include "gvplugin.h" + +extern gvplugin_installed_t gvdevice_types_glitz; + +static gvplugin_api_t apis[] = { + {API_device, &gvdevice_types_glitz}, + {(api_t)0, 0}, +}; + +gvplugin_library_t gvplugin_glitz_LTX_library = { "glitz", apis };