Logo Search packages:      
Sourcecode: xcircuit version File versions

render.c

/*----------------------------------------------------------------------*/
/* render.c --- Ghostscript rendering of background PostScript files    */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University            */
/* These routines work only if ghostscript is on the system.            */
/*----------------------------------------------------------------------*/

#undef GS_DEBUG
/* #define GS_DEBUG */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>   /* for waitpid() */
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>

/*------------------------------------------------------------------------*/
/* Local includes                                                         */
/*------------------------------------------------------------------------*/

#ifdef TCL_WRAPPER 
#include <tk.h>
#endif

#include "colordefs.h"
#include "xcircuit.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                                      */
/*----------------------------------------------------------------------*/
#include "prototypes.h"

/*------------------------------------------------------------------------*/
/* External Variable definitions                                          */
/*------------------------------------------------------------------------*/

extern char _STR2[250], _STR[150];
extern Globaldata xobjs;
extern Clientdata areastruct;
extern Display *dpy;
extern short eventmode;
extern int *appcolors;
extern int number_colors;
extern colorindex *colorlist;
extern Cursor appcursors[NUM_CURSORS];

Pixmap bbuf = (Pixmap)NULL;   /* background buffer */
#ifdef DOUBLEBUFFER
extern Pixmap dbuf;
#endif

#ifndef HAVE_VFORK
#define vfork fork
#endif

/*------------------------------------------------------*/
/* Global variable definitions                        */
/*------------------------------------------------------*/

Atom  gv, gvc, gvpage, gvnext, gvdone;
pid_t gsproc = -1;      /* ghostscript process              */
int   fgs[2];           /* stdin pipe pair for ghostscript  */
Window mwin = 0;  /* "false" window hack to get gs    */
                  /* process to capture ClientMessage */
                  /* events.                    */

/*--------------------------------------------------------------*/
/* Preliminary in allowing generic PostScript backgrounds   */
/* via the ghostscript interpreter:  Set the GHOSTVIEW and  */
/* GHOSTVIEW atoms, and set the GHOSTVIEW environtment            */
/* variable.                                          */
/*--------------------------------------------------------------*/

void ghostinit()
{
   gv = XInternAtom(dpy, "GHOSTVIEW", False);
   gvc = XInternAtom(dpy, "GHOSTVIEW_COLORS", False);
   gvpage = XInternAtom(dpy, "PAGE", False);
   gvnext = XInternAtom(dpy, "NEXT", False);
   gvdone = XInternAtom(dpy, "DONE", False);

   sprintf(_STR, "%ld %d %d %d %d %d %g %g %d %d %d %d",
            0L, 0, 0, 0,
            areastruct.width * 75 / 72,
            areastruct.height * 75 / 72,
            75.0, 75.0, 0, 0, 0, 0);
   XChangeProperty(dpy, areastruct.areawin, gv, XA_STRING, 8, PropModeReplace,
      _STR, strlen(_STR));
   sprintf(_STR, "%s %d %d", "Color", (int)FOREGROUND, (int)BACKGROUND);
   XChangeProperty(dpy, areastruct.areawin, gvc, XA_STRING, 8, PropModeReplace,
      _STR, strlen(_STR));
   XSync(dpy, False);
}

/*------------------------------------------------------*/
/* Send a ClientMessage event                   */
/*------------------------------------------------------*/

void send_client(Atom msg)
{
   XEvent event;

   if (mwin == 0) return;     /* Have to wait for gs */
                        /* to give us window # */

   event.xclient.type = ClientMessage;
   event.xclient.display = dpy;
   event.xclient.window = areastruct.areawin;
   event.xclient.message_type = msg;
   event.xclient.format = 32;
   event.xclient.data.l[0] = mwin;
   event.xclient.data.l[1] = bbuf;
   XSendEvent(dpy, mwin, False, 0, &event);
   XFlush(dpy);
}

/*------------------------------------------------------*/
/* Ask ghostscript to produce the next page.          */
/*------------------------------------------------------*/

void ask_for_next()
{
   XSync(dpy, False);
   send_client(gvnext);
#ifdef GS_DEBUG
   fprintf(stdout, "Xcircuit: Sent NEXT message to ghostscript\n");
#endif
}

/*------------------------------------------------------*/
/* Wait for the gs process to return "PAGE"           */
/* (Block until gs is finished)                       */
/*------------------------------------------------------*/

void wait_for_page()
{
   XEvent event;

   for(;;) {
      XNextEvent(dpy, &event);
      if (event.type == ClientMessage) {
       if (event.xclient.message_type == gvpage) break;
         else if (event.xclient.message_type == gvdone) {
#ifdef GS_DEBUG
          fprintf(stdout, "Xcircuit: Received DONE message from ghostscript\n");
#endif
          /* Catastrophic error in ghostscript */
          return;
         }
      } 
      else if (event.type == KeyPress) {  /* Check for Ctrl-C break */
       XKeyEvent *keyevent = (XKeyEvent *)(&event);
       if (keyevent->state == 4 && keyevent->keycode == 54) {
          Wprintf("Ctrl-C break from render loop");
          break;
       }
       else {
          Fprintf(stderr, "state = %d, keycode = %d\n", keyevent->state,
            keyevent->keycode);
          xcDispatchEvent(&event);
       }
      }
      else xcDispatchEvent(&event); 
   }
#ifdef GS_DEBUG
   fprintf(stdout, "Xcircuit: Received PAGE message from ghostscript\n");
#endif
   mwin = event.xclient.data.l[0];
}

/*------------------------------------------------------*/

void wait_for_done()
{
   XEvent event;

   for(;;) {
      XNextEvent(dpy, &event);
      if (event.type == ClientMessage) {
       if (event.xclient.message_type == gvdone) break;
#ifdef GS_DEBUG
         else if (event.xclient.message_type == gvpage)
          fprintf(stdout, "Xcircuit: Received PAGE message from ghostscript\n");
#endif
      }
      else xcDispatchEvent(&event);
   }
#ifdef GS_DEBUG
   fprintf(stdout, "Xcircuit: Received DONE message from ghostscript\n");
#endif
   mwin = 0;
}

/*--------------------------------------------------------*/
/* Start a ghostscript process and arrange the I/O pipes  */
/* (Commented lines cause ghostscript to relay its stderr */
/* to xcircuit's stderr)                          */
/*--------------------------------------------------------*/

void start_gs()
{
   int std_out[2], std_err[2], ret;
#ifdef HAVE_PUTENV
   static char env_str1[128], env_str2[64];
#endif

#ifdef TCL_WRAPPER
   if (bbuf != (Pixmap)NULL) Tk_FreePixmap(dpy, bbuf);
#ifdef DOUBLEBUFFER
   bbuf = Tk_GetPixmap(dpy, dbuf, areastruct.width, areastruct.height,
            Tk_Depth(areastruct.area));
#else
   bbuf = Tk_GetPixmap(dpy, (Drawable)areastruct.areawin,
            areastruct.width, areastruct.height,
            Tk_Depth(areastruct.area));
#endif

#else /* !TCL_WRAPPER */
   if (bbuf != (Pixmap)NULL) XFreePixmap(dpy, bbuf);
#ifdef DOUBLEBUFFER
   bbuf = XCreatePixmap(dpy, dbuf, areastruct.width, areastruct.height,
            DefaultDepthOfScreen(xcScreen(areastruct.area))); 
#else
   bbuf = XCreatePixmap(dpy, areastruct.areawin,
            areastruct.width, areastruct.height,
            DefaultDepthOfScreen(xcScreen(areastruct.area))); 
#endif      /* DOUBLEBUFFER */
#endif  /* TCL_WRAPPER */

   XSync(dpy, False);

   ret = pipe(fgs);
   ret = pipe(std_out);
#ifndef GS_DEBUG
   ret = pipe(std_err);
#endif

   /* We need a complicated pipe here, with input going from xcircuit   */
   /* to gs to provide scale/position information, and input going from */
   /* the background file to gs for rendering.                    */
   /* Here is not the place to do it.  Set up gs to take stdin as input.*/

   if (gsproc < 0) {  /* Ghostscript is not running yet */
      gsproc = vfork();
      if (gsproc == 0) {            /* child process (gs) */
#ifdef GS_DEBUG
         fprintf(stdout, "Calling %s\n", GS_EXEC);
#endif
       close(std_out[0]);
#ifndef GS_DEBUG
       close(std_err[0]);
#endif
       dup2(fgs[0], 0);
       close(fgs[0]);
       dup2(std_out[1], 1);
       close(std_out[1]);
#ifndef GS_DEBUG
       dup2(std_err[1], 2);
       close(std_err[1]);
#endif

#ifdef HAVE_PUTENV
       sprintf(env_str1, "DISPLAY=%s", XDisplayString(dpy));
       putenv(env_str1);
#ifdef DOUBLEBUFFER
       sprintf(env_str2, "GHOSTVIEW=%ld %ld", (long)areastruct.areawin, (long)bbuf);
       putenv(env_str2);
#else
       sprintf(env_str2, "GHOSTVIEW=%ld", (long)areastruct.areawin);
       putenv(env_str2);
#endif
#else
       setenv("DISPLAY", XDisplayString(dpy), True);
#ifdef DOUBLEBUFFER
       sprintf(_STR, "%ld %ld", (long)areastruct.areawin, (long)bbuf);
       setenv("GHOSTVIEW", _STR, True);
#else
       sprintf(_STR, "%ld", (long)areastruct.areawin);
       setenv("GHOSTVIEW", _STR, True);
#endif
#endif
       Flush(stderr);
         execlp(GS_EXEC, "gs", "-dNOPAUSE", "-", NULL);
       gsproc = -1;
       Fprintf(stderr, "Exec of gs failed\n");
       return;
      }
      else if (gsproc < 0) {
       Wprintf("Error: ghostscript not running");
       return;                /* error condition */
      }
   }
}

/*--------------------------------------------------------*/
/* Parse the background file for Bounding Box information */
/*--------------------------------------------------------*/

void parse_bg(FILE *fi, FILE *fbg) {
   char *bbptr;
   Boolean bflag = False;
   int llx, lly, urx, ury;
   char line_in[256];
   float psscale;

   psscale = getpsscale(xobjs.pagelist[areastruct.page]->outscale, areastruct.page);

   for(;;) {
      if (fgets(line_in, 255, fi) == NULL) {
         Wprintf("Error: end of file before end of insert.");
         return;
      }
      else if (strstr(line_in, "end_insert") != NULL) break;
      
      if (!bflag) {
       if ((bbptr = strstr(line_in, "BoundingBox:")) != NULL) {
          if (strstr(line_in, "(atend)") == NULL) {
             bflag = True;
             sscanf(bbptr + 12, "%d %d %d %d", &llx, &lly, &urx, &ury);
             /* compute user coordinate bounds from PostScript bounds */
#ifdef GS_DEBUG
             fprintf(stdout, "BBox %d %d %d %d PostScript coordinates\n",
                  llx, lly, urx, ury);
#endif
             llx = (int)((float)llx / psscale);
             lly = (int)((float)lly / psscale);
             urx = (int)((float)urx / psscale);
             ury = (int)((float)ury / psscale);
#ifdef GS_DEBUG
             fprintf(stdout, "BBox %d %d %d %d XCircuit coordinates\n",
                  llx, lly, urx, ury);
#endif
      
             xobjs.pagelist[areastruct.page]->background.bbox.lowerleft.x = llx;
             xobjs.pagelist[areastruct.page]->background.bbox.lowerleft.y = lly;
             xobjs.pagelist[areastruct.page]->background.bbox.width = (urx - llx);
             xobjs.pagelist[areastruct.page]->background.bbox.height = (ury - lly);
             if (fbg == (FILE *)NULL) break;
          }
       }
      }
      if (fbg != (FILE *)NULL) fputs(line_in, fbg);
   }
}

/*-------------------------------------------------------*/
/* Get bounding box information from the background file */
/*-------------------------------------------------------*/

void bg_get_bbox()
{
   FILE *fi;
   char *fname;

   fname = xobjs.pagelist[areastruct.page]->background.name;
   if ((fi = fopen(fname, "r")) == NULL) {
      Fprintf(stderr, "Failure to open background file to get bounding box info\n");
      return;
   }
   parse_bg(fi, (FILE *)NULL);
   fclose(fi);
}

/*------------------------------------------------------------*/
/* Adjust object's bounding box based on the background image */
/*------------------------------------------------------------*/

void backgroundbbox(int mpage)
{
   int llx, lly, urx, ury, tmp;
   objectptr thisobj = xobjs.pagelist[mpage]->pageinst->thisobject;
   psbkground *thisbg = &xobjs.pagelist[mpage]->background;

   llx = thisobj->bbox.lowerleft.x;
   lly = thisobj->bbox.lowerleft.y;
   urx = thisobj->bbox.width + llx;
   ury = thisobj->bbox.height + lly;

   if (thisbg->bbox.lowerleft.x < llx) llx = thisbg->bbox.lowerleft.x;
   if (thisbg->bbox.lowerleft.y < lly) lly = thisbg->bbox.lowerleft.y;
   tmp = thisbg->bbox.width + thisbg->bbox.lowerleft.x;
   if (tmp > urx) urx = tmp;
   tmp = thisbg->bbox.height + thisbg->bbox.lowerleft.y;
   if (tmp > ury) ury = tmp;

   thisobj->bbox.lowerleft.x = llx;
   thisobj->bbox.lowerleft.y = lly;
   thisobj->bbox.width = urx - llx;
   thisobj->bbox.height = ury - lly;
}

/*------------------------------------------------------*/
/* Read a background PostScript image from a file and */
/* store in a temporary file, passing that filename to      */
/* the background property of the page.               */
/*------------------------------------------------------*/

void readbackground(FILE *fi)
{
   FILE *fbg = (FILE *)NULL;
   int tfd;
   char *file_in = (char *)malloc(9 + strlen(xobjs.tempdir));

   /* "@" denotes a temporary file */
   sprintf(file_in, "@%s/XXXXXX", xobjs.tempdir);

   tfd = mkstemp(file_in + 1);
   if (tfd == -1) Fprintf(stderr, "Error generating temporary filename\n");
   else {
      if ((fbg = fdopen(tfd, "w")) == NULL) {
       Fprintf(stderr, "Error opening temporary file \"%s\"\n", file_in + 1);
      }
   }

   /* Read the file to the restore directive or end_insertion */
   /* Skip restore directive and end_insertion command */

   parse_bg(fi, fbg);

   if (fbg != (FILE *)NULL) {
      fclose(fbg);
      call_loadbg(file_in);
   }
   free(file_in);
}

/*------------------------------------------------------*/
/* Save a background PostScript image to the output   */
/* file by streaming directly from the background file      */
/*------------------------------------------------------*/

void savebackground(FILE *fo, char *psfilename)
{
   FILE *psf;
   char *fname = psfilename;
   char line_in[256];

   if (fname[0] == '@') fname++;

   if ((psf = fopen(fname, "r")) == NULL) {
      Fprintf(stderr, "Error opening background file \"%s\" for reading.\n", fname);
      return;
   }

   for(;;) {
      if (fgets(line_in, 255, psf) == NULL)
       break;
      else
       fputs(line_in, fo);
   }
   fclose(psf);
}

/*------------------------------------------------------*/
/* Load a generic (non-xcircuit) postscript file as the */
/* background for the page.                     */
/*------------------------------------------------------*/

void call_loadbg(char *gsfile)
{
   if (gsproc < 0)
      start_gs();
   else
      reset_gs();

   xobjs.pagelist[areastruct.page]->background.name =
            (char *) malloc(strlen(gsfile) + 1);
   strcpy(xobjs.pagelist[areastruct.page]->background.name, gsfile);
}

/*--------------------------------------------------------*/

void loadbackground()
{
   call_loadbg(_STR2);
   bg_get_bbox();
   updatepagebounds(topobject);
   zoomview(NULL, NULL, NULL);
}

/*------------------------------------------------------*/
/* Send text to the ghostscript renderer        */
/*------------------------------------------------------*/

void send_to_gs(char *text)
{
   write(fgs[1], text, strlen(text));
#ifdef GS_DEBUG
   fprintf(stdout, "writing: %s", text);
#endif
}

/*------------------------------------------------------*/
/* Call ghostscript to render the background          */
/*------------------------------------------------------*/

int renderbackground()
{
   char *bgfile;
   float psnorm, psxpos, psypos, defscale;
   float devres = 0.96; /* = 72.0 / 75.0, ps_units/in : screen_dpi */

   if (gsproc < 0) return -1;

   defscale = (xobjs.pagelist[areastruct.page]->coordstyle == CM) ?
      CMSCALE : INCHSCALE;

   psnorm = (*areastruct.vscale) * (1.0 / defscale) * devres;

   psxpos = (float)(-areastruct.pcorner->x) * (*areastruct.vscale) * devres;
   psypos = (float)(-areastruct.pcorner->y) * (*areastruct.vscale) * devres
            + ((float)areastruct.height / 12.0);

   /* Conditions for re-rendering:  Must have a background specified */
   /* and must be on the page, not a library or other object.          */

   if (xobjs.pagelist[areastruct.page]->background.name == (char *)NULL)
      return -1;
   else if (areastruct.lastbackground == xobjs.pagelist[areastruct.page]->background.name)
      return 0;

   if (is_page(topobject) == -1)
      return -1;

   ask_for_next();

   bgfile = xobjs.pagelist[areastruct.page]->background.name;
   if (*bgfile == '@') bgfile++;

#ifdef GS_DEBUG
   fprintf(stdout, "Rendering background from file \"%s\"\n", bgfile);
#endif

   /* write scale and position to ghostscript         */
   /* and tell ghostscript to run the requested file  */

/*   send_to_gs("/GSobj save def\n"); */
   send_to_gs("gsave\n");
   sprintf(_STR, "%3.2f %3.2f translate\n", psxpos, psypos);
   send_to_gs(_STR);
   sprintf(_STR, "%3.2f %3.2f scale\n", psnorm, psnorm);
   send_to_gs(_STR);
   sprintf(_STR, "(%s) run\n", bgfile);
   send_to_gs(_STR);
/* send_to_gs("GSobj restore\n"); */
   send_to_gs("grestore\n");

   /* ask ghostscript to produce the next page */

   XDefineCursor(dpy, areastruct.areawin, WAITFOR);
   Wprintf("Rendering background image.");
   wait_for_page();
   Wprintf("Background finished.");
   XDefineCursor(dpy, areastruct.areawin, CROSS);

   /* Mark this as the most recently rendered background, so we don't   */
   /* have to render more than necessary.                   */
   areastruct.lastbackground = xobjs.pagelist[areastruct.page]->background.name;

   return 0;
}

/*----------------------------------------------------------*/
/* Copy the rendered background pixmap to the buffer pixmap */
/*----------------------------------------------------------*/

int copybackground()
{
   /* only draw on a top-level page */
   if (is_page(topobject) == -1)
      return -1;

#ifdef DOUBLEBUFFER
   XCopyArea(dpy, bbuf, dbuf, areastruct.gc, 0, 0,
             areastruct.width, areastruct.height, 0, 0);
#else
   XCopyArea(dpy, bbuf, areastruct.areawin, areastruct.gc, 0, 0,
             areastruct.width, areastruct.height, 0, 0);
#endif

   return 0;
}

/*------------------------------------------------------*/
/* Exit ghostscript. . . gently                       */
/*------------------------------------------------------*/

int exit_gs()
{
   if (gsproc < 0) return -1; /* gs not running */

   send_to_gs("quit\n");
   ask_for_next();
#ifdef GS_DEBUG
   fprintf(stdout, "Waiting for gs to exit\n");
#endif
   waitpid(gsproc, NULL, 0);      
#ifdef GS_DEBUG
   fprintf(stdout, "gs has exited\n");
#endif

   mwin = 0;
   gsproc = -1;

   return 0;
}

/*------------------------------------------------------*/
/* Restart ghostscript                          */
/*------------------------------------------------------*/

int reset_gs()
{
   if (gsproc < 0) return -1;

   exit_gs();
   ghostinit();
   start_gs();

   return 0;
}

/*----------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index