Back to index

lightning-sunbird  0.9+nobinonly
Classes | Functions
xprintutil_printtofile.c File Reference
#include "xprintutil.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>

Go to the source code of this file.

Classes

struct  MyPrintFileData
 XpuPrintToFile() - fork() version Create consumer thread which creates it's own display connection to print server (a 2nd display connection/process is required to avoid deadlocks within Xlib), registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and processes/eats all incoming events via MyPrintToFileProc(). More...

Functions

static Bool XNextEventTimeout (Display *display, XEvent *event_return, struct timeval *timeout)
static voidXpuPrintToFile (Display *pdpy, XPContext pcontext, const char *filename)
static void MyPrintToFileProc (Display *pdpy, XPContext pcontext, unsigned char *data, unsigned int data_len, XPointer client_data)
static void MyFinishProc (Display *pdpy, XPContext pcontext, XPGetDocStatus status, XPointer client_data)
static voidPrintToFile_Consumer (void *handle)
void XpuStartJobToSpooler (Display *pdpy)
voidXpuStartJobToFile (Display *pdpy, XPContext pcontext, const char *filename)
XPGetDocStatus XpuWaitForPrintFileChild (void *handle)

Class Documentation

struct MyPrintFileData

XpuPrintToFile() - fork() version Create consumer thread which creates it's own display connection to print server (a 2nd display connection/process is required to avoid deadlocks within Xlib), registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and processes/eats all incoming events via MyPrintToFileProc().

A final call to MyPrintToFileProc() cleans-up all stuff and sets the "done" flag. Note that these callbacks are called directly by Xlib while waiting for events in XNextEvent() because XpGetDocumentData() registeres them as "internal" callbacks, e.g. XNextEvent() does not return before/after processing these events !!

Usage: XpStartJob(pdpy, XPGetData); handle = XpuPrintToFile(pdpy, pcontext, "myfile"); // render something XpEndJob(); // or XpCancelJob() status = XpuWaitForPrintFileChild(handle);

Definition at line 286 of file xprintutil_printtofile.c.

Collaboration diagram for MyPrintFileData:
Class Members
const char * displayname
Bool done
FILE * file
const char * file_name
Display * parent_pdpy
XPContext pcontext
Display * pdpy
pid_t pid
int pipe
XPGetDocStatus status

Function Documentation

static void MyFinishProc ( Display pdpy,
XPContext  pcontext,
XPGetDocStatus  status,
XPointer  client_data 
) [static]

Definition at line 410 of file xprintutil_printtofile.c.

{
  MyPrintFileData *mpfd = (MyPrintFileData *)client_data;

  /* remove the file if unsuccessful */
  if( status != XPGetDocFinished )
  {
    XPU_DEBUG_ONLY(printf("MyFinishProc: error %d\n", (int)status));
    XPU_TRACE_CHILD(remove(mpfd->file_name));
  }  
  
  XPU_TRACE_CHILD((void)fclose(mpfd->file)); /* what about error handling ? */
  
  mpfd->status = status; 
  mpfd->done   = True;    
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void MyPrintToFileProc ( Display pdpy,
XPContext  pcontext,
unsigned char *  data,
unsigned int  data_len,
XPointer  client_data 
) [static]

Definition at line 396 of file xprintutil_printtofile.c.

{
  MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
  
  /* write to the file */
  XPU_TRACE_CHILD((void)fwrite(data, data_len, 1, mpfd->file)); /* what about error handling ? */
}   

Here is the call graph for this function:

Here is the caller graph for this function:

static void * PrintToFile_Consumer ( void handle) [static]

Definition at line 435 of file xprintutil_printtofile.c.

{
  MyPrintFileData *mpfd = (MyPrintFileData *)handle;
  XEvent           dummy;
  struct timeval   timeout;
  
  timeout.tv_sec  = 0;
  timeout.tv_usec = 100000; /* 1/10 s */
        
  XPU_DEBUG_ONLY(printf("### child running, getting data from '%s'.\n", mpfd->displayname));
  
  /* we cannot reuse fork()'ed display handles - our child needs his own one */
  if( (mpfd->pdpy = XPU_TRACE_CHILD(XOpenDisplay(mpfd->displayname))) == NULL )
  {
    perror("child cannot open display");
#ifdef XPU_USE_NSPR
    return;
#else
    return(NULL);
#endif
  }
    
  mpfd->done = False;
    
  /* register "consumer" callbacks */
  if( XPU_TRACE_CHILD(XpGetDocumentData(mpfd->pdpy, mpfd->pcontext,
                                        MyPrintToFileProc, MyFinishProc, 
                                        (XPointer)mpfd)) == 0 )
  {
    XPU_DEBUG_ONLY(printf("XpGetDocumentData cannot register callbacks\n"));
#ifdef XPU_USE_NSPR
      return;
#else
      return(NULL);
#endif
  }      
  
  /* loop forever - libXp has registered hidden event callbacks for the consumer 
   * callbacks - the finishCB will call set the "done" boolean after all...
   */
  while( mpfd->done != True )
  {
    XNextEventTimeout(mpfd->pdpy, &dummy, &timeout);
  }
  
  XCloseDisplay(mpfd->pdpy);

#ifdef XPU_USE_THREADS
#ifdef XPU_USE_NSPR
  return;
#else
  return(NULL);
#endif
#else   
  /* write the status to the parent */
  if( XPU_TRACE_CHILD(write(mpfd->pipe[1], &mpfd->status, sizeof(XPGetDocStatus))) != sizeof(XPGetDocStatus) )
  {
    perror("PrintToFile_Consumer: can't write XPGetDocStatus");
  }
  
  /* we don't do any free's or close's, as we are just
   * going to exit, in fact, get out without calling any C++
   * destructors, etc., as we don't want anything funny to happen
   * to the parent 
   */
   XPU_TRACE_CHILD(_exit(EXIT_SUCCESS));
#endif /* XPU_USE_THREADS */           
}

Here is the call graph for this function:

Here is the caller graph for this function:

static Bool XNextEventTimeout ( Display display,
XEvent *  event_return,
struct timeval timeout 
) [static]

Definition at line 111 of file xprintutil_printtofile.c.

{
  int      res;
  fd_set   readfds;
  int      display_fd = XConnectionNumber(display);

  /* small shortcut... */
  if( timeout == NULL )
  {
    XNextEvent(display, event_return);
    return(True);
  }
  
  FD_ZERO(&readfds);
  FD_SET(display_fd, &readfds);

  /* Note/bug: In the case of internal X events (like used to trigger callbacks 
   * registered by XpGetDocumentData()&co.) select() will return with "new info" 
   * - but XNextEvent() below processes these _internal_ events silently - and 
   * will block if there are no other non-internal events.
   * The workaround here is to check with XEventsQueued() if there are non-internal 
   * events queued - if not select() will be called again - unfortunately we use 
   * the old timeout here instead of the "remaining" time... (this only would hurt 
   * if the timeout would be really long - but for current use with values below
   * 1/2 secs it does not hurt... =:-)
   */
  while( XEventsQueued(display, QueuedAfterFlush) == 0 )
  {
    res = select(display_fd+1, &readfds, NULL, NULL, timeout);
  
    switch(res)
    {
      case -1: /* select() error - should not happen */ 
          perror("XNextEventTimeout: select() failure"); 
          return(False);
      case  0: /* timeout */
        return(False);
    }
  }
  
  XNextEvent(display, event_return); 
  return(True);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void * XpuPrintToFile ( Display pdpy,
XPContext  pcontext,
const char *  filename 
) [static]

Definition at line 302 of file xprintutil_printtofile.c.

{
  MyPrintFileData *mpfd;

  if( (mpfd = (MyPrintFileData *)malloc(sizeof(MyPrintFileData))) == NULL )
    return(NULL);

  /* create pipe */
  if( pipe(mpfd->pipe) == -1 ) 
  {
    /* this should never happen, but... */
    perror("XpuPrintToFile: cannot create pipe");
    free(mpfd);
    return(NULL);
  }
        
  mpfd->parent_pdpy = pdpy;
  mpfd->displayname = XDisplayString(pdpy);
  mpfd->pcontext    = pcontext;
  mpfd->file_name   = filename;
  mpfd->file        = NULL;
  mpfd->status      = XPGetDocError;
  
  /* make sure we can open the file for writing */
  if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL ) 
  {
    /* fopen() error */
    close(mpfd->pipe[1]);
    close(mpfd->pipe[0]);      
    free(mpfd);
    return(NULL);
  }
  
  /* its important to flush before we fork, to make sure that the
   * XpStartJob gets through first in the parent
   */
  XFlush(pdpy);     
  
  mpfd->pid = fork();

  if( mpfd->pid == 0 ) 
  {
    /* we're now in the fork()'ed child */
    PrintToFile_Consumer(mpfd);
  }
  else if( mpfd->pid < 0 ) 
  {
    /* fork() error */
    close(mpfd->pipe[1]);
    close(mpfd->pipe[0]);
    fclose(mpfd->file);
    free(mpfd);
    return(NULL);
  }
  
  /* we're still in the parent */
  XPU_DEBUG_ONLY(printf("### parent fork()'ed consumer child.\n"));
  
  /* child will write into file - we don't need it anymore here... :-) */
  fclose(mpfd->file);
  close(mpfd->pipe[1]);
  return(mpfd);      
}

Here is the call graph for this function:

Here is the caller graph for this function:

void* XpuStartJobToFile ( Display pdpy,
XPContext  pcontext,
const char *  filename 
)

Definition at line 77 of file xprintutil_printtofile.c.

{
  void *handle;

  XpStartJob(pdpy, XPGetData);
  handle = XpuPrintToFile(pdpy, pcontext, filename);
  
  if (!handle)
  {
    /* Cancel the print job and discard all events... */
    XpCancelJob(pdpy, True);
  }

  return(handle);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 72 of file xprintutil_printtofile.c.

{
  XpStartJob(pdpy, XPSpool);
}

Here is the caller graph for this function:

XPGetDocStatus XpuWaitForPrintFileChild ( void handle)

Definition at line 367 of file xprintutil_printtofile.c.

{
  MyPrintFileData *mpfd   = (MyPrintFileData *)handle;
  int              res;
  XPGetDocStatus   status = XPGetDocError; /* used when read() from pipe fails */

  /* Flush data a last time to make sure we send all data to Xprt and that
   * the Xlib internal hooks have called a last time */
  XFlush(mpfd->parent_pdpy);
  
  if( XPU_TRACE(waitpid(mpfd->pid, &res, 0)) == -1 )
    perror("XpuWaitForPrintFileChild: waitpid failure");

  /* read the status from the child */ 
  if( read(mpfd->pipe[0], &status, sizeof(XPGetDocStatus)) != sizeof(XPGetDocStatus) )
  {
    perror("XpuWaitForPrintFileChild: can't read XPGetDocStatus");
  }    
  close(mpfd->pipe[0]);
    
  free(handle);
  
  XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
  return(status);
}

Here is the caller graph for this function: